Announcement
- Programmed on
2024/3/1
- Written on
2024/3/2
题目来源
Description
给定一个仅由数字构成的字符串
s
s
s,用 ,
将其划分为若干个正整数,使其严格递增。
求在满足最后一个数字最小的情况下,字典序最大(第一个数最大,在此基础上第二个数最大……)的方案。
- ∣ s ∣ ≤ 500 |s|\le500 ∣s∣≤500。
Solution
设 f [ i ] f[i] f[i] 表示把前 i i i 个数按照条件划分后,最后一个数字最大的起始下标为 f [ i ] f[i] f[i](此时最后一个最小)。
若将
s
[
x
⋯
y
]
s[x\cdots y]
s[x⋯y] 表示为
s
s
s 中下标属于
x
x
x 到
y
y
y 的字符组成的数字,转移方程:
f
[
i
]
=
max
{
j
∣
j
∈
[
1
,
i
]
∩
Z
,
s
[
f
[
j
−
1
]
⋯
j
−
1
]
<
s
[
j
⋯
i
]
}
f[i]=\max\{j\big|j\in[1,i]\cap\Z,s[f[j-1]\cdots j-1]<s[j\cdots i]\}
f[i]=max{j
j∈[1,i]∩Z,s[f[j−1]⋯j−1]<s[j⋯i]}
初始化
f
[
1
]
=
1
f[1]=1
f[1]=1,这样我们就求出了最后一个数的最小值,接着考虑字典序的限制。
设 g [ i ] g[i] g[i] 表示把 [ i , n ] [i,n] [i,n] 按照条件划分后,在保证最后一个数字为之前求出的最小值 s [ f [ n ] ⋯ n ] s[f[n]\cdots n] s[f[n]⋯n] 的前提下,第一个数字最大的终止下标为 g [ i ] g[i] g[i](此时第一个数最大)。
转移方程:
g
[
i
]
=
max
{
j
∣
j
∈
[
i
,
∣
s
∣
]
∩
Z
,
s
[
i
⋯
j
]
<
s
[
j
+
1
,
g
[
j
+
1
]
]
}
g[i]=\max\{j\big|j\in\big[i,|s|\big]\cap\Z,s[i\cdots j]<s[j+1,g[j+1]]\}
g[i]=max{j
j∈[i,∣s∣]∩Z,s[i⋯j]<s[j+1,g[j+1]]}
正序求
f
[
i
]
f[i]
f[i],再倒序求
g
[
i
]
g[i]
g[i]。
初始化 g [ f [ n ] ] = n g[f[n]]=n g[f[n]]=n,注意 f [ n ] f[n] f[n] 前面连续的一段零也需要令其 g [ i ] = n g[i]=n g[i]=n,因为前导零对最后一个数字的大小无影响,且还能使 g [ i ] g[i] g[i] 变大,从而更有可能转移。
最后输出即可。
比较两个字符串需要 O ( ∣ s ∣ ) O(|s|) O(∣s∣),枚举 f [ i ] , g [ i ] f[i],g[i] f[i],g[i] 的下标需要 O ( ∣ s ∣ ) O(|s|) O(∣s∣),转移枚举需要 O ( ∣ s ∣ ) O(|s|) O(∣s∣),总的需要 O ( ∣ s ∣ 3 ) O(|s|^3) O(∣s∣3)。
Code
#include <bits/stdc++.h>
using namespace std;
int n,f[505],g[505]; // f[i]-把s[1~i]划分为递增数列,最后一个数最小为s[f[i]~i];g[i]-在最后一个数最小的情况之下,把s[i~n]划分为递增数列,第一个数最大为s[i~g[i]]
char s[505];
string get(int x,int y){
bool Flag=0;
string res="";
for (int i=x;i<=y;i++){
if (s[i]!='0') Flag=1;
if (Flag) res+=s[i];
}
return res;
}
bool cmp(string x,string y){
if (x.size()!=y.size()) return x.size()<y.size();
return x<y;
}
int main(){
scanf(" %s",s+1),n=strlen(s+1);
f[1]=1;
for (int i=2;i<=n;i++) for (int j=i;j;j--) if (cmp(get(f[j-1],j-1),get(j,i))){f[i]=j;break;}
int t=0;
for (int i=f[n]-1;i;i--)
if (s[i]=='0') g[i]=n;
else{t=i;break;}
g[f[n]]=n;
for (int i=t;i>0;i--) for (int j=f[n]-1;j>=i;j--) if (cmp(get(i,j),get(j+1,g[j+1]))){g[i]=j;break;}
for (int i=1;i<=n;i=g[i]+1){
for (int j=i;j<=g[i];j++) putchar(s[j]);
if (g[i]!=n) putchar(',');
}
return 0;
}