(字符串dp)P1415 拆分数列

https://www.luogu.org/problemnew/show/P1415
给出一列数字,需要你添加任意多个逗号将其拆成若干个严格递增的数。如果有多组解,则输出使得最后一个数最小的同时,字典序最大的解(即先要满足最后一个数最小;如果有多组解,则使得第一个数尽量大;如果仍有多组解,则使得第二个数尽量大,依次类推……)。
3456
3,4,5,6
3546
35,46
0001
0001

第一次做字符串dp的题,没得头绪。。。
看了下题解,发现要把问题分解,先解决最后一个数最小的问题,再解决字典序最大的问题

#include<bits/stdc++.h>
using namespace std;
const int maxn = 500 + 5;
char s[maxn];
int num[maxn], f[maxn], dp[maxn], t1[maxn], t2[maxn];
int cmp(int l1, int r1, int l2, int r2) {
    //3->above, 2->beyond, 1->equal
    if(r2 == 0) return 3;
    memset(t1, 0, sizeof(t1));
    memset(t2, 0, sizeof(t2));
    int len1 = 0, len2 = 0;
    for(int i = r1; i >= l1; i--)
        t1[++len1] = num[i];
    for(int i = r2; i >= l2; i--)
        t2[++len2] = num[i];
    int maxlen = max(len1, len2);
    for(int i = maxlen; i > 0; i--) {
        if(t1[i] != t2[i]) {
            if(t1[i] > t2[i]) return 3;
            if(t1[i] < t2[i]) return 2;
        }
    }
    return 1;
}
int main()
{
    cin >> s;
    int len = 0;
    for(int i = 0; s[i] != '\0'; i++)
        num[i + 1] = s[i] - '0', len++;
    //正向dp找出最后一个最小的数的状态
    //f[i]保存的是右端点是i时最优解的左端点位置
    //f[i] = max(j) | T(f[j - 1], j - 1) < T(j, i)
    //即j-i的值要严格大于f[j - 1] - j-1才满足递增
    for(int i = 1; i <= len; i++) {
        f[i] = 1;
        for(int j = i; j >= 1; j--) {
            if(cmp(j, i, f[j - 1], j - 1) == 3) {
                f[i] = max(f[i], j);
                break;
            }
        }
    }
    dp[f[len]] = len;
    int cnt = 0 ;
    for(int i = f[len] - 1; i > 0 && !num[i]; i--) {
        dp[i] = len;
        cnt++;
    }
    //反向dp求字典序最大的解
    //dp[i]保存的是以i开始的最优解的右端点位置
    for(int i = f[len] - 1 - cnt; i > 0; i--) {
        dp[i] = i;
        for(int j = f[len] - 1; j >= i; j--) {
            if(cmp(i, j, j + 1, dp[j + 1]) == 2) {
                dp[i] = max(dp[i], j);
                break;
            }
        }
    }
    int pos = 1;
    bool flag = true;
    while(pos <= len) {
        if(flag) flag = false;
        else putchar(',');
        for(int i = pos; i <= dp[pos]; i++) putchar(num[i] + '0');
        pos = dp[pos] + 1;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值