導彈攔截(最長子序列問題,二分查找)
阿新 • • 發佈:2018-10-18
eight str 單調性 當前位置 防禦 攔截 ios 輸入輸出格式 fine
題目描述
某國為了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
輸入導彈依次飛來的高度(雷達給出的高度數據是≤50000的正整數),計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。
輸入輸出格式
輸入格式:
111行,若幹個整數(個數≤10000)
輸出格式:
2行,每行一個整數,第一個數字表示這套系統最多能攔截多少導彈,第二個數字表示如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。
思路:
題意已經明顯告訴你讓你求一個最長不上升子序列和一個最長上升子序列
O(n^2)的算法大家應該都會,現在的問題是如何優化
我們以第一問為例
首先記下在當前位置之前長度為k的最長不下降子序列的結尾最大是什麽(如果沒有,就記為0)
然後,我們得出一個單調性:長度越長,必然高度越低
(因為一個較長的必定由一個較短的轉移而來,由最長不上升子序列的性質可得,後者高度小於等於前者高度)
我們就可以用二分查找的方式找到轉移點轉移
不過記住,轉移後要更新長度為當前位置長度的最長不下降子序列的結尾
代碼:
#include<iostream> #include<cstdio> #include<cstring> #define rii register int i #define rij register int j using namespace std; int dp[100005],n,m,cd[100005],h[100005],ans; int main() { for(rii=0;i<=100005;i++) { cd[i]=-1; } cd[0]=2147483647; n=1; while(~scanf("%d",&h[n])) { n++; } n--; for(rii=1;i<=n;i++) { int l=0,r=i; while(l<r) { if(r-l==1) { if(cd[r]>=h[i]) { dp[i]=r+1; cd[r+1]=max(cd[r+1],h[i]); } else { dp[i]=l+1; cd[l+1]=max(cd[l+1],h[i]); } break; } int mid=(l+r)/2; if(cd[mid]<h[i]) { r=mid; } else { l=mid; } } ans=max(ans,dp[i]); } cout<<ans<<endl; for(rii=0;i<=100005;i++) { cd[i]=2147483647; } ans=0; memset(dp,0,sizeof(dp)); cd[0]=0; for(rii=1;i<=n;i++) { int l=0,r=i; while(l<r) { if(r-l==1) { if(cd[r]<h[i]) { dp[i]=r+1; cd[r+1]=min(cd[r+1],h[i]); } else { dp[i]=l+1; cd[l+1]=min(cd[l+1],h[i]); } break; } int mid=(l+r)/2; if(cd[mid]>=h[i]) { r=mid; } else { l=mid; } } ans=max(ans,dp[i]); } cout<<ans; }
導彈攔截(最長子序列問題,二分查找)