天天看點

【HDU6357】Hills And Valleys(DP)

題意

給定一個數組

A[]

,元素均在[0,9],可以翻轉一段區間,求最長不下降子序列,并輸出翻轉區間

題解

很巧妙的DP

因為值域很小,我們可以枚舉需要翻轉的區間值出一個數組

B[]

,規劃好我們要求得的序列的大緻趨向。

如我們翻轉的區間的值為[7,3],則建構B數組為

B[]={0,1,2,3,7,6,5,4,3,7,8,9}

,dp時按照B數組中的元素,進行轉移。

定義dp狀态:

dp[i][j]

為比對到A串的第i位,B串的第j位,最長不下降子序列的長度:

轉移為

dp[i][j] = min ( dp[i][j-1], dp[i-1][j]+(A[i]==B[j]) )

dp[i][j-1]

轉移是為了保證j越大,

dp[i][j]

也越大,使得當i固定,

dp[i][j]

保持不下降

dp[i-1][j]

轉移即在A串中添加一位

A[i]

,如果

A[i]

值在比對的B串位置之後,則最長不下降序列可增加一位,因為我們已經保證

dp[i][j]>=dp[i][j-1]

,是以隻用判斷

A[i]==B[j]

即可。

因為需要輸出翻轉方案,我們用

path[i][j]

數組記錄dp轉移路徑,dp完成後,利用path恢複我們從B中選出來的元素(作為最長不下降子序列的元素),存在數組

C[i]

裡,然後判斷從哪一位開始翻轉即可。

注意:如果沒有翻轉,需把翻轉區間長度設為1(自己跟自己翻轉)

代碼

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=,MAXL=;

int N,M;
int A[MAXN],B[MAXL];
int dp[MAXN][MAXL];
pair<int,int> path[MAXN][MAXL],C[MAXN];

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(int i=;i<=N;i++)
            scanf("%1d",&A[i]);
        int ans=,ansl,ansr;
        for(int x=;x<;x++)
            for(int y=x;y<;y++)
            {
                M=;
                int l,r;
                for(int i=;i<=x;i++)
                    B[++M]=i;
                l=M+;
                for(int i=y;i>=x;i--)
                    B[++M]=i;
                r=M;
                for(int i=y;i<;i++)
                    B[++M]=i;
                for(int i=;i<=N;i++)
                    for(int j=;j<=M;j++)
                    {
                        if(dp[i][j-]<dp[i-][j]+(A[i]==B[j]))
                        {
                            dp[i][j]=dp[i-][j]+(A[i]==B[j]);
                            path[i][j]=make_pair(i-,j);
                        }
                        else
                        {
                            dp[i][j]=dp[i][j-];
                            path[i][j]=make_pair(i,j-);
                        }
                    }
                if(dp[N][M]>ans)
                {
                    ans=dp[N][M];
                    int p=N,q=M,top=ans;
                    ansl=-,ansr=-;
                    pair<int,int> t;
                    while(p&&q)
                    {
                        t=path[p][q];
                        if(dp[t.first][t.second]+==dp[p][q]&&A[p]==B[q])
                            C[top--]=make_pair(p,q);
                        p=t.first;
                        q=t.second;
                    }
                    int k;
                    for(k=;k<=ans&&C[k].second<l;k++);
                    ansl=C[k].first;
                    for(;k<=ans&&C[k].second<=r;k++);
                    ansr=C[k-].first;
                    if(ansl>ansr)
                        ansl=ansr=;
                }
            }
        //printf("%d\n",ans);
        printf("%d %d %d\n",ans,ansl,ansr);
    }

    return ;
}