luoguP1415 拆分數列
阿新 • • 發佈:2019-01-22
題目背景
【為了響應黨中央勤節儉、反鋪張的精神,題目背景描述故事部分略去^-^】
題目描述
給出一列數字,需要你新增任意多個逗號將其拆成若干個嚴格遞增的數。如果有多組解,則輸出使得最後一個數最小的同時,字典序最大的解(即先要滿足最後一個數最小;如果有多組解,則使得第一個數儘量大;如果仍有多組解,則使得第二個數儘量大,依次類推……)。
輸入輸出格式
輸入格式:
共一行,為初始的數字。
輸出格式:
共一行,為拆分之後的數列。每個數之間用逗號分隔。行尾無逗號。
輸入輸出樣例
輸入樣例#1: 複製
[1]
3456
[2]
3546
[3]
3526
[4]
0001
[5]
100000101
輸出樣例#1: 複製
[1]
3,4,5,6
[2]
35,46
[3]
3,5,26
[4]
0001
[5]
100,000101
說明
【題目來源】
lzn改編
【資料範圍】
對於10%的資料,輸入長度<=5
對於30%的資料,輸入長度<=15
對於50%的資料,輸入長度<=50
對於100%的資料,輸入長度<=500
題解
因為要保證最後一位最小,先正向dp一遍求一下最後一個數最小是多少,
然後在從這個數反著求一下最遠的可轉移到的點(保證這一位儘量大)
方程應該很明顯
設f[i]為從1到i的合法序列,離i最近的分割點
有
設g[i]為從i到n的合法序列,離i最遠的分割點
有
注意處理前導0
ps:字串處理好鵝心,跟dp難度一比這就是到字串處理..
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct BIG{
int len;
int a[510];
BIG(){
len=0;
memset(a,0,sizeof(a));
}
bool operator <(const BIG &b)const{
if(len==0||b.len==0)
return false;
if(len!=b.len)
return len<b.len;
int nw=1;
while(nw<=len&&a[nw]==b.a[nw])
nw++;
if(nw==len+1)
return 0;
return a[nw]<b.a[nw];
}
};
int f[510];
char A[510];
int ans[510];
int g[510];
inline BIG get(int l,int r){
while(A[l]=='0'&&l<=r)
l++;
BIG g;
g.len=r-l+1;
for(int i=l;i<=r;i++)
g.a[i-l+1]=A[i]-'0';
return g;
}
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%s",A+1);
int n=strlen(A+1);
memset(f,127,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=i;j>=1;j--){
BIG x=get(f[j-1],j-1),y=get(j,i);
if(x<y){
f[i]=j;
break;
}
}
}
int tot=0;
int u=f[n];
memset(g,127,sizeof(g));
g[u]=n;
u--;
for(int i=u;i>=1;i--){
g[i]=n;
for(int j=u;j>=i;j--)
if(get(i,j)<get(j+1,g[j+1])){
g[i]=j;
break;
}
}
int t=1;
while(t<=u){
ans[++tot]=g[t];
t=g[t]+1;
}
//ans[++tot]=u;
ans[++tot]=n;
for(int i=1;i<=ans[1];i++)
printf("%c",A[i]);
if(ans[1]!=n)
printf(",");
for(int i=1;i<tot;i++){
for(int j=ans[i]+1;j<=ans[i+1];j++)
printf("%c",A[j]);
if(ans[i+1]!=n)
printf(",");
}
return 0;
}