2018 Multi-University Training Contest 5 1008 / hdu6357 Hills And Valleys LCS,思维
Hills And Valleys
题意:
给出长度为 n 的数字串,要你选定一个区间 [l,r] 并翻转这个区间内的所有数字,翻转后使得最长非递减子序列的长度最大。求出长度、翻转的区间 [l,r] 。
1<=l<=r<=n。
官方题解:
枚举 [x,y] 表示翻转区间 [l,r] 中对答案产生贡献的数字所处值域,然后找出 A 最长的类似0\∗1\∗⋯x\∗y\∗(y−1)\∗⋯x\∗y\∗(y+1)\∗⋯9\∗ 的子序列,其中 k\∗ 表示任意非负整数个 k。
参考了大佬的题解https://blog.csdn.net/qq_34454069/article/details/81475646
1、因为只有10 个数字,所以可以把求最长非递减子序列 转为 求原序列与 序列{0,1,2.......8,9} 的最长公共子序列。注:第二个序列可以重复使用。
2、把翻转区间转化为枚举数字值域[x,y],翻转就可以在第二个序列里翻转。即翻转 [x,y],第二个序列就变为了 {0,1,2..x, y,y-1,y-2....x+1,x, y,y+1.....9} ,然后再求LCS。
3、要求的翻转区间 [l,r] ,我们在求 LCS 时记录即可。
好像也可以 dp 写。。。
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long ll;
const int N = 100005, M = 15;
int dp[N][M], tl[N][M], tr[N][M];
int n, m, ans, ansl, ansr;
int spl, spr;
char s[N], A[N];
void solve()
{
rep(j,0,m) dp[0][j] = 0;
rep(i,1,n) rep(j,1,m)
{
dp[i][j] = dp[i-1][j], tl[i][j] = tl[i-1][j], tr[i][j] = tr[i-1][j];
if(s[i] == A[j]) {
++dp[i][j];
if(j==spl && tl[i][j]==0) tl[i][j] = i;
if(j==spr) tr[i][j] = i;
}
if(dp[i][j] < dp[i][j-1]) {
dp[i][j] = dp[i][j-1], tl[i][j] = tl[i][j-1], tr[i][j] = tr[i][j-1];
}
}
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
int chMin=9, chMax=0;
scanf("%d%s", &n, s+1);
m = 9;
rep(i,0,m) A[i] = '0'+i;
solve();
ans = dp[n][m], ansl = ansr = 1;
rep(low,0,9) rep(up,low,9)
{
m = 0;
rep(i,0,low) A[++m] = '0'+i;
spl = m+1;
per(i,up,low) A[++m] = '0'+i;
spr = m;
rep(i,up,9) A[++m] = '0'+i;
solve();
if(ans<dp[n][m] && tl[n][m] && tr[n][m])
ans = dp[n][m], ansl = tl[n][m], ansr = tr[n][m];
}
printf("%d %d %d\n", ans, ansl, ansr);
}
return 0;
}