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;
}