DP - LIS - 攔截導彈 - NOIP1999

DP - LIS - 攔截導彈 - NOIP1999

某國爲了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。

但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。

某天,雷達捕捉到敵國的導彈來襲。

由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。

輸入導彈依次飛來的高度(雷達給出的高度數據是不大於30000的正整數,導彈數不超過1000),計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。

輸入格式
共一行,輸入導彈依次飛來的高度。

輸出格式
第一行包含一個整數,表示最多能攔截的導彈數。

第二行包含一個整數,表示要攔截所有導彈最少要配備的系統數。

輸入樣例:
389 207 155 300 299 170 158 65
輸出樣例:
6
2


分析:

給定一個序列,要求兩個問題:\\①、最長非遞增子序列的長度。\\②、原序列最少能夠分成幾個最長非遞增子序列。

要覆蓋掉整個原序列,那麼每個數都要被覆蓋掉\\因此,可以從貪心的角度思考,每次去除的非遞增子序列一定要儘量的長,去除的次數就是最終答案。\\即對每個元素而言,要讓該元素後面能夠接的元素儘量的多。

調這個思想與單調隊列優化最長上升子序列是一致的。

=有結論:最長上升子序列的方案數=序列能夠分成的最長非遞增子序列的個數。

具體落實:

aig[k]:i1kaigaig[k]aig[k]ai,kai求方案數:\\對每個元素a_i,數組g[k]:前i-1個元素已經構成的第k個非遞增子序列的末尾元素。\\對於a_i,我們遍歷數組g,找到第一個末尾元素大於a_i的元素g[k],將a_i接在其後,那麼此時g[k]更新爲a_i,\\也就是說第k個非遞增子序列的末尾元素變成了a_i。

最後非遞增子序列的個數就是最終的方案數。

拿樣例舉例:
i=1g[0]=389i=1:g[0]=389
i=2g[0]=207i=2:g[0]=207
i=3g[0]=155i=3:g[0]=155
i=4g[0]=155g[1]=300i=4:g[0]=155,g[1]=300
i=5g[0]=155g[1]=299i=5:g[0]=155,g[1]=299
i=6g[0]=155g[1]=170i=6:g[0]=155,g[1]=170
i=7g[0]=155g[1]=158i=7:g[0]=155,g[1]=158
i=8g[0]=65g[1]=158i=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;
    
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章