1. 程式人生 > >單調序列 解題報告

單調序列 解題報告

單調序列

\(\tt{GISPZJZ}\) 有一個長度為 \(n\) 的序列 \(a_1,a_2,\dots,a_n\)。 序列的所有元素都是 \(1\) 或者 \(2\)
我們稱一序列是該序列的不下降子序列 \(p_1,p_2,\dots,p_k\), 滿足 \(1\le p_1<p_2<p_3<\dots <p_k\le n\), 且\(a_{p_1}\le a_{p_2}\le \dots \le a_{p_n}\)
現在 \(\tt{GISPZJZ}\) 可以選擇序列中的一段區間\([L,R]\), 然後將整段反轉, 例如挑選區間\([2,4]\),可以將序列\((a_1,a_2,a_3,a_4,a_5)\)

變換為\((a_1,a_4,a_3,a_2,a_5)\)。 在此基礎上, \(\tt{GISPZJZ}\) 希望在反轉後, 序列的最長不下降子序列最長。 當然, \(\tt{GISPZJZ}\) 也可以選擇不反轉任何區間。 現在要求出最優情況下, 序列的最長不下降子序列的長度。

輸入:

第一行一個正整數 \(n\)
第二行 \(n\) 個數, 分別為 \(a_1,a_2,\dots,a_n\), 滿足 \(1\le a_i\le 2\)

輸出:

一行一個正整數 \(x\), 表示答案。

資料範圍:

對於\(10\%\)的資料, \(1\le n\le 10\)
對於\(40\%\)

的資料, \(1\le n\le 200\)
對於\(70\%\)的資料, \(1\le n \le 2000\)
對於\(100\%\)的資料, \(1\le n\le 100000\)


思路還是很巧妙的。

最開始想把一串的縮在一起做,發現討論起來非常麻煩。

觀察了一下發現翻轉後不就是把\(000111\)什麼的變成了可以選\(000111000111\)這樣類似的了嗎?

於是直接劃分一下狀態\(\tt{DP}\)即可,是一個非常好的思路啊。


Code:

#include <cstdio>
const int N=1e5+10;
int a[N],dp[N][4],n,ans;
int max(int x,int y){return x>y?x:y;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",a+i),--a[i];
    for(int i=1;i<=n;i++)
    {
        dp[i][0]=dp[i-1][0]+(a[i]==0);
        dp[i][1]=max(dp[i-1][0],dp[i-1][1])+(a[i]==1);
        dp[i][2]=max(dp[i-1][1],dp[i-1][2])+(a[i]==0);
        dp[i][3]=max(dp[i-1][2],dp[i-1][3])+(a[i]==1);
        ans=max(max(dp[i][0],dp[i][1]),max(dp[i][2],dp[i][3]));
    }
    printf("%d\n",ans);
    return 0;
}

2017.11.2