1. 程式人生 > >(字串dp)P1415 拆分數列

(字串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;
}