洛谷 P1020 導彈攔截 菜鳥題解

Asura_Y發表於2020-11-11

有志者,事竟成,破釜沉舟,百二秦關終屬楚。
苦心人,天不負,臥薪嚐膽,三千越甲可吞吳。

P1020 導彈攔截

題目連結:

https://www.luogu.com.cn/problem/P1020

在這裡插入圖片描述

題目解釋:

首先就是要找到一個最長非上升子序列,這是第一問,第二問就是非上升序列有多少個。

問題分析:

就是優化最長非上升子序列的問題,要求時間複雜度 n l o g n nlog_n nlogn n 2 n^2 n2的時間複雜度會爆,所以得優化。

題目思路:

首先新建一個陣列dp[],和變數int,分別用來記錄非上升子序列,和當前最長非上升子序列的長度。

看樣例:

389 207 155 300 299 170 158 65
首先存在最長非上升子序列:389 300 299 170 158 65, 長度為6
其中207 155,這個序列需要第二套裝置
所以答案就是6 2

首先開始手動模擬

記住這是動態規劃題目,dp陣列只需要儲存一種狀態而不是準確的答案。

dp陣列中儲存一個非上升子序列,當出現一個小於 d p [ l e n ] dp[len] dp[len]的數字的時候,就執行 d p [ + + l e n ] = a [ i ] dp[++len] = a[i] dp[++len]=a[i],這時表明,當前最長長度發生變化,更新當前最長長度的最小值。

如果出現一個大於 d p [ l e n ] dp[len] dp[len]的數字 a [ i ] a[i] a[i]的時候,就使用二分查詢,在dp陣列中找到一個大於 a [ i ] a[i] a[i]的位置,然後把那個位置的數字替換為 a [ i ] a[i] a[i]

這意味著,如果後面出了剛好比 a [ i ] a[i] a[i]小的序列,我就可以把原來的dp陣列給替換了,而替換的過程len的大小並不變。如果到時候len值變化了,說明一定有更長的新的序列出現了。如果我一直在替換dp陣列中的,但是len值沒變化,其實這個時候就相當於,我取得最長非上升子序列還是len值剛剛更新時的那個dp陣列。

第一個問題解決。

第二個問題:

怎麼解決多個非上升子序列:

和上一問思路差不多。新建一個陣列dp2[],然後只要有小的數字出現,就替換,如果有大的數字出現,就加在後面,有小的數字出現即 d p 2 [ l e n 2 ] > = a [ i ] dp2[len2] >= a[i] dp2[len2]>=a[i],說明當前的裝置可以滿足,裝置數量並不需要滿足,如果有大的數字出現即 d p 2 [ l e n 2 ] < a [ i ] dp2[len2] < a[i] dp2[len2]<a[i],說明當前的高度已經無法滿足,需要另外一套新的裝置,套用上一問的思路,直接二分就可以。

AC程式碼:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 7;
int f[N], d1[N], d2[N];

int main(){
    ios::sync_with_stdio(false);
    int n = 0, len1 = 1, len2 = 1;
    while (cin >> f[++n]);  n--;
    d1[1] = d2[1] = f[1];
    for (int i = 2; i <= n; i++){
        //只要高度比當前裝置最後一次的高度低,那就可以繼續使用
        if (d1[len1] >= f[i])   d1[++len1] = f[i];
        else{
            int p = upper_bound(d1 + 1, d1 + 1 + len1, f[i], greater<int> ()) - d1;
            d1[p] = f[i];
        }
        //只要高度不提高,就不需要另外一套裝置
        if (d2[len2] < f[i])    d2[++len2] = f[i];
        else{
            int p = lower_bound(d2 + 1, d2 + 1 + len2, f[i]) - d2;
            d2[p] = f[i];
        }
    }
    cout << len1 << " " << len2 << endl;
    return 0;
}

需要注意兩個函式:
lower_bound(),找到第一個大於等於的數的位置,返回值為指標,並不是下標所以要減去首地址。
upper_bound(),找到第一個大於的數的位置,返回值同樣為指標。

相關文章