Description
给出一个长度为 的数字串,可以将其中一段翻转,问翻转后该数字串的非严格最长上升子序列长度最大值
Input
第一行一整数 表示用例组数,每组用例输入一整数 和一个长度为 的数字串
Output
输出翻转后该串的最长上升子序列长度最大值以及要翻转的区间端点
Sample Input
2
9
864852302
9
203258468
Sample Output
5 1 8
6 1 2
Solution
考虑反转部分两端点的值以及第一部分结束部分的值,以 表示用前 个数字构造且以 结尾,使得第一部分结束值不超过 ,反转部分左端点值不超过 ,右端点值不小于 的前两部分最长长度, 记录该状态最优解的中间反转部分左端点编号,那么每次 有两种情况,第一种是接到之前反转部分右边,那么此时 应该为 ,这样有转移 ,此时反转部分左端点不变,第二种是从 开始反转,那么 ~ 这部分就成为第一部分,以 表示前 个数字以一个不超过 的数字结尾的最长上升子序列长度,以 表示以 中若干字母组成以一个不小于 的数字开始的最长上升子序列长度,那么此时有转移 ,注意如果更新了 要同步更新左端点 ,在求出所有 后,答案即为
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;
}