HDU 6357 Hills And Valleys(dp)

202 篇文章 1 订阅

Description

给出一个长度为 n n 的数字串,可以将其中一段翻转,问翻转后该数字串的非严格最长上升子序列长度最大值

Input

第一行一整数T表示用例组数,每组用例输入一整数 n n 和一个长度为n的数字串

(1T100,1n105,n2105) ( 1 ≤ T ≤ 100 , 1 ≤ n ≤ 10 5 , ∑ n ≤ 2 ⋅ 10 5 )

Output

输出翻转后该串的最长上升子序列长度最大值以及要翻转的区间端点

Sample Input

2
9
864852302
9
203258468

Sample Output

5 1 8
6 1 2

Solution

考虑反转部分两端点的值以及第一部分结束部分的值,以 dp[i][x][y][z] d p [ i ] [ x ] [ y ] [ z ] 表示用前 i i 个数字构造且以ai结尾,使得第一部分结束值不超过 x x ,反转部分左端点值不超过z,右端点值不小于 y y 的前两部分最长长度,l[i][x][y][z]记录该状态最优解的中间反转部分左端点编号,那么每次 ai a i 有两种情况,第一种是接到之前反转部分右边,那么此时 ai a i 应该为 y y ,这样有转移dp[i][x][y][z]=dp[i1][x][y][z]+(ai=y),此时反转部分左端点不变,第二种是从 ai a i 开始反转,那么 1 1 ~i1这部分就成为第一部分,以 L[i][x] L [ i ] [ x ] 表示前 i i 个数字以一个不超过x的数字结尾的最长上升子序列长度,以 R[i][x] R [ i ] [ x ] 表示以 ai,...,an a i , . . . , a n 中若干字母组成以一个不小于 x x 的数字开始的最长上升子序列长度,那么此时有转移dp[i][x][y][z]=max(dp[i][x][y][z],L[i1][x]+(ai=y)),注意如果更新了 dp[i][x][y][z] d p [ i ] [ x ] [ y ] [ z ] 要同步更新左端点 l[i][x][y][z]=i l [ i ] [ x ] [ y ] [ z ] = i ,在求出所有 dp[i][x][y][z] d p [ i ] [ x ] [ y ] [ z ] 后,答案即为 max(dp[i][x][y][z]+R[i+1][z]) m a x ( d p [ i ] [ x ] [ y ] [ z ] + R [ i + 1 ] [ z ] )

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 100005
int T,n,L[maxn][10],R[maxn][10],dp[2][10][10][10],l[2][10][10][10];
char s[maxn];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%s",&n,s+1);
        for(int i=1;i<=n;i++)s[i]-='0';
        memset(L[0],0,sizeof(L[0]));
        memset(R[n+1],0,sizeof(R[n+1]));
        for(int i=1;i<=n;i++)
            for(int j=0;j<=9;j++)
                L[i][j]=max(j>0?L[i][j-1]:0,L[i-1][j]+(s[i]==j));
        for(int i=n;i>=1;i--)
            for(int j=9;j>=0;j--)
                R[i][j]=max(j<9?R[i][j+1]:0,R[i+1][j]+(s[i]==j));
        memset(dp,0,sizeof(dp));
        memset(l,0,sizeof(l));
        int ans=0,ansl=1,ansr=1;
        for(int i=1;i<=n;i++)
        {
            for(int x=0;x<=9;x++)
                for(int z=x;z<=9;z++)
                    for(int y=z;y>=x;y--)
                    {
                        dp[i&1][x][y][z]=dp[(i-1)&1][x][y][z];
                        l[i&1][x][y][z]=l[(i-1)&1][x][y][z];
                        dp[i&1][x][y][z]+=(s[i]==y);
                        if(y<z&&dp[i&1][x][y][z]<dp[i&1][x][y+1][z])
                        {
                            dp[i&1][x][y][z]=dp[i&1][x][y+1][z];
                            l[i&1][x][y][z]=l[i&1][x][y+1][z];
                        }
                        if(dp[i&1][x][y][z]<L[i-1][x]+(s[i]==y))
                        {
                            dp[i&1][x][y][z]=L[i-1][x]+(s[i]==y);
                            l[i&1][x][y][z]=i;
                        }
                    }
                for(int x=0;x<=9;x++)
                    for(int z=x;z<=9;z++)
                        for(int y=z;y>=x;y--)
                            if(ans<dp[i&1][x][y][z]+R[i+1][z])
                            {
                                ans=dp[i&1][x][y][z]+R[i+1][z];
                                ansl=l[i&1][x][y][z],ansr=i;
                            }
        }
        printf("%d %d %d\n",ans,max(1,ansl),ansr);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值