DP - LIS - 攔截導彈 - NOIP1999
某國爲了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。
但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。
某天,雷達捕捉到敵國的導彈來襲。
由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
輸入導彈依次飛來的高度(雷達給出的高度數據是不大於30000的正整數,導彈數不超過1000),計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。
輸入格式
共一行,輸入導彈依次飛來的高度。
輸出格式
第一行包含一個整數,表示最多能攔截的導彈數。
第二行包含一個整數,表示要攔截所有導彈最少要配備的系統數。
輸入樣例:
389 207 155 300 299 170 158 65
輸出樣例:
6
2
分析:
給定一個序列,要求兩個問題:①、最長非遞增子序列的長度。②、原序列最少能夠分成幾個最長非遞增子序列。
要覆蓋掉整個原序列,那麼每個數都要被覆蓋掉因此,可以從貪心的角度思考,每次去除的非遞增子序列一定要盡量的長,去除的次數就是最終答案。即對每個元素而言,要讓該元素後面能夠接的元素盡量的多。
這個思想與單調隊列優化最長上升子序列是一致的。
有結論:最長上升子序列的方案數=序列能夠分成的最長非遞增子序列的個數。
具體落實:
求方案數:對每個元素ai,數組g[k]:前i−1個元素已經構成的第k個非遞增子序列的末尾元素。對於ai,我們遍歷數組g,找到第一個末尾元素大於ai的元素g[k],將ai接在其後,那麼此時g[k]更新爲ai,也就是說第k個非遞增子序列的末尾元素變成了ai。
最後非遞增子序列的個數就是最終的方案數。
拿樣例舉例:
i=1:g[0]=389
i=2:g[0]=207
i=3:g[0]=155
i=4:g[0]=155,g[1]=300
i=5:g[0]=155,g[1]=299
i=6:g[0]=155,g[1]=170
i=7:g[0]=155,g[1]=158
i=8:g[0]=65,g[1]=158
代碼:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int n,a[N],f[N],g[N];
int main()
{
while(cin>>a[++n]);
int res=0;
for(int i=1;i<n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
if(a[i]<=a[j])
f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
}
cout<<res<<endl;
int cnt=0;
for(int i=1;i<n;i++)
{
int k=0;
while(k<cnt&&g[k]<a[i]) k++;
g[k]=a[i];
if(k>=cnt) cnt++;
}
cout<<cnt<<endl;
return 0;
}