【DP】HDU6357 Hills And Valleys

分析:

真是石乐志的一道题目啊。。。。

这题我考场上现推的DP和官方题解一点关系都没有。。。。

还是说说官方题解的做法吧

它把最长不下降子序列映射成两个序列的最长公共子序列问题
a序列就是给出的原序列
b序列是值域的序列
需要注意的是:b序列可以重复匹配

一般的最长不下降子序列中,b序列就是:0,1,2,3,4,5,6,7,8,9
这样a和b的最长公共子序列就是一个最长不下降子序列。

然后这题它说可以翻转一次。我们发现如果在a序列中枚举翻转端点是很难实现的。但可以在b序列上枚举翻转端点(最多 C ( 10 , 2 ) 种方案)。
换句话说,我们可以枚举翻转的两个端点的值。
然后,b序列可以转化成这个样子:
假设我们枚举的翻转的左端点值为 y ,右端点值为 x ,满足 x < y
b序列就可以变成:
0 , 1 , 2 , x 1 , x , ( y , y 1 , y 2 , x + 1 , x ) , y , y + 1 , 8 , 9 其中括号内的部分可以通过翻转使得整个串仍然是一个0到9的不下降序列。

然后转移随便写写就可以了。。。
注意不要memset,容易超时

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
int n,t;
char s[MAXN];
int b[MAXN],spl,spr,ansl,ansr;
int dp[MAXN][22],tl[MAXN][22],tr[MAXN][22];;
int solve(int cnt){
    //memset(tl,0,sizeof tl);
    //memset(tr,0,sizeof tr);
    for(int i=0;i<cnt;i++)
        dp[0][i]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<cnt;j++){
            dp[i][j]=dp[i-1][j];
            tl[i][j]=tl[i-1][j];
            tr[i][j]=tr[i-1][j];
            if(s[i]==b[j]){
                dp[i][j]++;
                if(spl==j&&tl[i][j]==0)
                    tl[i][j]=i;
                if(spr==j)
                    tr[i][j]=i;
            }
            if(dp[i][j-1]>dp[i][j]){
                dp[i][j]=dp[i][j-1];
                tl[i][j]=tl[i][j-1];
                tr[i][j]=tr[i][j-1];
            }
        }
    return dp[n][cnt-1];
}
int main(){
    freopen("1008.in","r",stdin);
    int t;
    SF("%d",&t);    
    while(t--){
        SF("%d",&n);
        SF("%s",s+1);
        int minl=9;
        int maxl=0;
        for(int i=1;i<=n;i++){
            s[i]-='0';
            maxl=max(maxl,int(s[i]));
            minl=min(minl,int(s[i]));
        }
        for(int i=0;i<10;i++)
            b[i]=i;
        int ans=solve(10);
        ansl=1;
        ansr=1;
        for(int l=minl;l<=maxl;l++)
            for(int r=minl;r<l;r++){
                int cnt=0;
                for(int i=0;i<=r;i++)
                    b[cnt++]=i;
                spl=cnt;
                for(int i=l;i>=r;i--)
                    b[cnt++]=i;
                spr=cnt-1;
                for(int i=l;i<10;i++)
                    b[cnt++]=i;
                int ans1=solve(cnt);
                if(ans1>ans&&tl[n][cnt-1]&&tr[n][cnt-1]){
                    ans=ans1;
                    ansl=tl[n][cnt-1];
                    ansr=tr[n][cnt-1];  
                }
            }
        PF("%d %d %d\n",ans,ansl,ansr);
    }

}

猜你喜欢

转载自blog.csdn.net/qq_34454069/article/details/81475646
今日推荐