1. 程式人生 > >洛谷P1020

洛谷P1020

題目描述

某國為了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。

輸入導彈依次飛來的高度(雷達給出的高度資料是\le 50000≤50000的正整數),計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。

輸入輸出格式

輸入格式:

 

11行,若干個整數(個數\le 100000≤100000)

 

輸出格式:

 

22行,每行一個整數,第一個數字表示這套系統最多能攔截多少導彈,第二個數字表示如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。

i 1 2 3 4 5 6 7 8

a 389 207 155 300 299 170 158 65

f 1 2 3 2 3 4 5 6

發現當f的值相同時,越後面的導彈高度越高

用d[i]維護f值為i的最後一個導彈的位置,t記錄當前已經求出最長不升子序列長度

遞推求f時列舉a[d[t]],a[d[t-1]],。。。,a[d[1]]是否≥當前求的導彈高度,是就更新f

剛剛學會的求上升子序列的演算法面對100000個的數字時肯定是不行的,所以我看了看題解,學到了一種更快的方法。犧牲空間來壓縮時間吧,多定義一個數組來儲存當前最長子序列值所對應的序列號,就是i,從鍵盤輸入的數串第一位開始迴圈,f[i]儲存前i個的最大序列長度。初始化所有f[i]為1,再進行一層for迴圈,從當前i的最大子序列長度j往回迴圈到0,若a[i]<=a[d[j]];d[j]是當前j(i的最大子序列長度)所對應的i,則f[i]=j+1;緊接著用break結束迴圈,這是減少計算量的關鍵。。然後t=max(t,f[i]);他是最長子序列長度,d[f[i]]=i;記錄f[i]對應的i。。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;

int n=0,a[100001],f[100001],d[100001],ans=1,t=0;//t記錄當前已經求出最長不升子序列長度
int main() {
    while(~scanf("%d",&a[++n]));
    f[0] = 1;
    for (int i = 1;i<n;i++){
        f[i]=1;
        for(int j=t; j>0; j--)
            if(a[i]<=a[d[j]]) {//t一定小於i,若a[i]小於當前的則符合降序子序列。。。。反向迴圈是因為之前的最長子序列求出來了遇見的第一個必定是最大的,所以加一break
                f[i]=j+1;
                break;
            }
        t=max(t,f[i]);//f[i]是i的不升子序列長度
        d[f[i]]=i;//d[i的不升子序列長度]=i        d[]是記錄當前的i
    }
    cout << *max_element(f, f + n) << endl;
    t=0;
    for(int i=1; i<n; i++) {
        f[i]=1;
        for (int j = t;j>0;j--)
            if(a[i]>a[d[j]]){
                f[i] = f[d[j]] + 1;
                break;
            }
        t = max(t, f[i]);
        d[f[i]] = i;
    }
    cout << *max_element(f, f + n) << endl;
    return 0;
}