HDU 6357 Hills And Valleys(思维 动态规划)

题目链接:Hills And Valleys

题意

给定一个长度为 n n 的序列,可以选择序列上的一个区间 [l,r] (1lrn),将这个区间上的所有数字翻转,求进行一次操作后能够得到最长非递减子序列的长度。

输入

第一行为一个整数 T (1T100) T   ( 1 ≤ T ≤ 100 ) ,接下去有 T T 组数据,每组数据第一行为一个整数 n (1n105),第二行包含一个长度为 n n 的数字字符串 A1A2An (0Ai9)

输出

输出经过一次交换后能够得到的最长非递减子序列的长度,以及翻转区间的左右端点。

样例

输入
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 位与第二个序列前 j 位的最大匹配长度,时间复杂度为 O(nm) O ( n m ) m m 为第二个序列的长度。
如果原序列翻转最长非递减子序列的区间为 [l,r],则第二个序列应构造为 012(l1)lr(r1)(r2)(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 是因为如果翻转的子序列区间为 [3,7] [ 3 , 7 ] ,那么在第一个 7 7 之前的那部分 3 在翻转后也对最长非递减子序列有贡献,如果直接忽略这个 3 3 ,那部分贡献就没有算上去,会导致漏算。
最后输出翻转的区间,只要将翻转区间跟着 dp 一起更新即可,用 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值