题目链接:Hills And Valleys
题意
给定一个长度为 n n 的序列,可以选择序列上的一个区间 ,将这个区间上的所有数字翻转,求进行一次操作后能够得到最长非递减子序列的长度。
输入
第一行为一个整数 T (1≤T≤100) T ( 1 ≤ T ≤ 100 ) ,接下去有 T T 组数据,每组数据第一行为一个整数 ,第二行包含一个长度为 n n 的数字字符串 。
输出
输出经过一次交换后能够得到的最长非递减子序列的长度,以及翻转区间的左右端点。
样例
输入 |
---|
2 9 864852302 9 203258468 |
输出 |
5 1 8 6 1 2 |
提示 |
第一组数据将
864852302
864852302
区间
[1,8]
[
1
,
8
]
内的数字翻转后结果为
032584682
032584682
,其最长非递减子序列长度
03588
03588
; 第二组数据将 203258468 203258468 区间 [1,2] [ 1 , 2 ] 内的数字翻转后结果为 023258468 023258468 ,其最长非递减子序列长度 023588 023588 。 |
题解
假设将最长非递减子序列中的每段连续相同数字都缩成一个数字,如: 0123456789 0123456789 ,则问题可以转化为求原序列与这个序列的最长“公共”子序列,这个序列中的每个数字可以匹配零次或多次,这个问题可以用 dp d p 来解决,状态定义为 dp[i][j] d p [ i ] [ j ] ,表示第一个序列前 i i 位与第二个序列前 位的最大匹配长度,时间复杂度为 O(nm) O ( n m ) , m m 为第二个序列的长度。
如果原序列翻转最长非递减子序列的区间为 ,则第二个序列应构造为 012⋯(l−1)lr(r−1)(r−2)⋯(l+1)lr(r+1)⋯89 012 ⋯ ( l − 1 ) l r ( r − 1 ) ( r − 2 ) ⋯ ( l + 1 ) l r ( r + 1 ) ⋯ 89 ,如翻转区间为 [3,7] [ 3 , 7 ] ,就要构造 012376543789 0123 76543 789 ,再与第一个序列进行最长“公共”子序列匹配, 7 7 之前的 是因为如果翻转的子序列区间为 [3,7] [ 3 , 7 ] ,那么在第一个 7 7 之前的那部分 在翻转后也对最长非递减子序列有贡献,如果直接忽略这个 3 3 ,那部分贡献就没有算上去,会导致漏算。
最后输出翻转的区间,只要将翻转区间跟着 一起更新即可,用 dpl d p l 和 dpr d p r 表示 dp d p 取得最大值时翻转的区间,其中 dpl d p l 在多个 dp d p 取最大值时应取最靠前的位置, dpr d p r 只要跟着更新,就可以得到最后一个翻转的位置。
过题代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <functional>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn = 100000 + 100;
int T, n, ans, ansl, ansr;
char str[maxn], stmp[20];
int dp[maxn][20], dpl[maxn][20], dpr[maxn][20];
int solve(int l, int r, int len) {
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= len; ++j) {
dp[i][j] = dp[i - 1][j];
dpl[i][j] = dpl[i - 1][j];
dpr[i][j] = dpr[i - 1][j];
if(str[i] == stmp[j]) {
++dp[i][j];
if(j == l && dpl[i][j] == 0) {
dpl[i][j] = i;
}
if(j == r) {
dpr[i][j] = i;
}
}
if(dp[i][j] < dp[i][j - 1]) {
dp[i][j] = dp[i][j - 1];
dpl[i][j] = dpl[i][j - 1];
dpr[i][j] = dpr[i][j - 1];
}
}
}
return dp[n][len];
}
void Create(int l, int r) {
int Index = 1;
for(int i = 0; i <= l; ++i) {
stmp[Index++] = i + '0';
}
for(int i = r; i >= l; --i) {
stmp[Index++] = i + '0';
}
for(int i = r; i < 10; ++i) {
stmp[Index++] = i + '0';
}
}
int main() {
#ifdef LOCAL
freopen("test.txt", "r", stdin);
// freopen("testout.txt", "w", stdout);
#endif // LOCAL
ios::sync_with_stdio(false);
scanf("%d", &T);
while(T--) {
scanf("%d%s", &n, str + 1);
for(int i = 0; i < 10; ++i) {
stmp[i + 1] = i + '0';
}
ans = solve(0, 0, 10);
ansl = ansr = 1;
for(int i = 0; i < 10; ++i) {
for(int j = i + 1; j < 10; ++j) {
Create(i, j);
int tmp = solve(i + 2, j + 2, 12);
if(tmp > ans && dpl[n][12] != 0 && dpr[n][12] != 0) {
ans = tmp;
ansl = dpl[n][12];
ansr = dpr[n][12];
}
}
}
printf("%d %d %d\n", ans, ansl, ansr);
}
return 0;
}