題目來源:http://ybt.ssoier.cn:8088/problem_show.php?pid=1260
1260:【例9.4】攔截導彈(Noip1999)
時間限制: 1000 ms 內存限制: 65536 KB
提交數: 4533 通過數: 1660
【題目描述】
某國為了防御敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以后每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
輸入導彈依次飛來的高度(雷達給出的高度數據是不大於30000的正整數,導彈數不超過1000),計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。
【輸入】
輸入導彈依次飛來的高度。
【輸出】
第一行:最多能攔截的導彈數;
第二行:要攔截所有導彈最少要配備的系統數。
【輸入樣例】
389 207 155 300 299 170 158 65
【輸出樣例】
6 2
例題不怎么詳的解:
這是一道十分經典的LIS動規題目,難點和重點在於需要查找多條LIS並進行統計。這題說是dp,實則更像是純模擬。
我們抽絲剝繭,將本題數學框架討論如下:
首先我們要求的這個最多能攔截的導彈數,也就是給出序列的LIS,
然后是攔截所有導彈最少要配備的系統數,也就是最少有幾條公共子序列存在,注意,每條子序列必須是最優的。
這個最優是什么意思呢?也就是每套系統(每條子序列)必須攔截下最多的導彈(擁有最長最優的長度)。
本題的算法,我借鑒的是書上的解法,這個解法是可以優化空間復雜度的,但是因為我是初學者,所以寫出代碼后也不大懂優化,只能勉勉強強按書上的來。
第一問可以用dp解,第二問可以用貪心解。
算法分析:
設置a[j]代表原序列中第j個元素,b[j]表示長度為j的LIS,h[k]表示第k個系統當前可攔截導彈的最高高度;
- 遍歷已輸入序列一次,maxx暫存當前導彈高度可用最長LIS的長度值,於是當前輸入的導彈會使LIS長度maxx+1,並將此值存入b數組;
- 記錄最長LIS的長度;
- 貪心計算本次導彈由哪一套系統攔截,若當前所有LIS均對當前輸入導彈高度不可用,則新增一套系統攔截。
如果沒看懂也無妨,后面會詳細解釋。
重頭戲來了。
先初始化。
dp部分很簡單的:
1 maxx=0; 2 for(j=1;j<=i-1;j++) 3 if(a[j]>=a[i]&&b[j]>maxx) maxx=b[j]; 4 b[i]=maxx+1; 5 if(b[i]>m) m=b[i];
大概就是每輸入一個數,遍歷一遍當前序列,找一遍當前可構成的不下降序列。
當然如果僅僅是這樣就只能求出LIS,求不出最少的系統數,於是我們需要一個貪心:
1 x=0;//當前使用的系統 2 for(k=1;k<=n;k++) 3 { 4 if(h[k]>=a[i])//首先你這個系統你得可用 5 if(x==0) x=k; 6 else if(h[k]<h[x]) x=k;//選擇當前可攔截高度最低的可用系統攔截 7 } 8 if(x==0){ 9 n++;x=n; 10 } 11 h[x]=a[i];
完美。
樣例代碼:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<string> #include<cstdlib> #include<queue> #include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 10010
#define MOD 2520
#define E 1e-12
/*This is an example*/
int i,j,k,x,n,maxx,m,a[N],b[N],h[N]; using namespace std; int main() { i=1;n=0;m=0; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(h,0,sizeof(h)); while(cin>>a[i]) { maxx=0; for(j=1;j<=i-1;j++) if(a[j]>=a[i]&&b[j]>maxx) maxx=b[j]; b[i]=maxx+1; if(b[i]>m) m=b[i]; x=0; for(k=1;k<=n;k++) { if(h[k]>=a[i]) if(x==0) x=k; else if(h[k]<h[x]) x=k; } if(x==0){ n++;x=n; } h[x]=a[i]; i++; } cout<<m<<endl<<n<<endl; return 0; }
2019-05-03 12:34:13