(字串dp)P1415 拆分數列
阿新 • • 發佈:2018-11-28
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; }