hdu 6357 Hills And Valleys (DP)

http://acm.hdu.edu.cn/showproblem.php?pid=6357

题意:给定一个长度为n只包含‘0’~‘9’的字符串,你可以翻转[l,r]的区间一次,然后求它的最长不下降子序列的长度的最大值,并求出翻转区间。

思路:

我们可以构造一个b序列为0123456789,然后让a序列和b序列来匹配,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,注意翻转的左右两个端点要记录两次,然后枚举翻转区间找到最大值,即可。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define ll long long
int dp[maxn][22],L[maxn][22],R[maxn][22];
char a[maxn];
int b[maxn];
int c[maxn];
int n,cnt,tl,tr,ans,ansl,ansr,ma,mi;
int work()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<cnt;j++)
        {
            L[i][j]=0,R[i][j]=0;
        }
    }
    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];
            L[i][j]=L[i-1][j];
            R[i][j]=R[i-1][j];
            if(b[i]==c[j])
            {
                dp[i][j]++;
                if(tl==j&&!L[i][j])
                    L[i][j]=i;
                if(tr==j)
                    R[i][j]=i;
            }
            if(dp[i][j-1]>dp[i][j]&&j>=1)
            {
                dp[i][j]=dp[i][j-1];
                L[i][j]=L[i][j-1];
                R[i][j]=R[i][j-1];
            }
        }
    }
    return dp[n][cnt-1];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        scanf("%s",a+1);
        ma=0;
        mi=9;
        for(int i=1;i<=n;i++)
        {
            b[i]=a[i]-'0';
            ma=max(b[i],ma);
            mi=min(b[i],mi);
        }
        cnt=0;
        for(int i=0;i<10;i++)
            c[cnt++]=i;
        int ans=work();
        ansl=1;
        ansr=1;
        for(int i=mi;i<=ma;i++)
        {
            for(int j=mi;j<i;j++)
            {
                cnt=0;
                for(int k=0;k<=j;k++)
                    c[cnt++]=k;
                tl=cnt;
                for(int k=i;k>=j;k--)
                    c[cnt++]=k;
                tr=cnt-1;
                for(int k=i;k<10;k++)
                    c[cnt++]=k;
                int ans1=work();
                if(ans1>ans&&L[n][cnt-1]&&R[n][cnt-1])
                {
                    ans=ans1;
                    ansl=L[n][cnt-1];
                    ansr=R[n][cnt-1];
                }
            }
        }
        printf("%d %d %d\n",ans,ansl,ansr);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/imzxww/article/details/81545951