1. 程式人生 > >luoguP1415 拆分數列

luoguP1415 拆分數列

題目背景

【為了響應黨中央勤節儉、反鋪張的精神,題目背景描述故事部分略去^-^】

題目描述

給出一列數字,需要你新增任意多個逗號將其拆成若干個嚴格遞增的數。如果有多組解,則輸出使得最後一個數最小的同時,字典序最大的解(即先要滿足最後一個數最小;如果有多組解,則使得第一個數儘量大;如果仍有多組解,則使得第二個數儘量大,依次類推……)。

輸入輸出格式

輸入格式:
共一行,為初始的數字。

輸出格式:
共一行,為拆分之後的數列。每個數之間用逗號分隔。行尾無逗號。

輸入輸出樣例

輸入樣例#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最近的分割點

f[i]=max(j)[num(f[j1],j1)<num(j,i),j<=i]
設g[i]為從i到n的合法序列,離i最遠的分割點

g
[i]=max(j)[num(i,j)<num(j+1,g[j+1]),j>=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; }