首页 > 代码库 > luoguP1415 拆分数列 [dp]

luoguP1415 拆分数列 [dp]

题目描述

给出一列数字,需要你添加任意多个逗号将其拆成若干个严格递增的数。如果有多组解,则输出使得最后一个数最小的同时,字典序最大的解(即先要满足最后一个数最小;如果有多组解,则使得第一个数尽量大;如果仍有多组解,则使得第二个数尽量大,依次类推……)。

输入输出格式

输入格式:

共一行,为初始的数字。

输出格式:

共一行,为拆分之后的数列。每个数之间用逗号分隔。行尾无逗号。

输入输出样例

输入样例#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


 

《拆分数列》解题报告

By lzn 动态规划常规题。

第一步先求出最后的那个数最小为多少。(为了叙述方便,记T(i,j)表示从原数列下标i取到j的数字组成的数。)只需正向dp一次,dp1[i]表示前i个数字分成任意多个递增数且最后的数最小时,最后的数为T(dp1[i],i)。则dp1[i]=max(j),(T(dp1[j-1],j-1)<T(j,i))。

第二步要求最后一个数确定的情况下,前面的数字按字典序尽量大的解。类似上面的方法反向动归一次即可。

算法复杂度o(l^3)。由于数据大部分为随机,实际运行效率接近l^2。

 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<string> 5 using namespace std; 6  7 const int maxn=505; 8  9 string str;10 int a[maxn],n,dP[maxn],Dp[maxn];11 12 bool cmp(int l1,int r1,int l2,int r2){13     while(l1<=r1&&a[l1]==0)  l1++;14     while(l2<=r2&&a[l2]==0)  l2++;15     int le1=r1-l1+1,le2=r2-l2+1;16     if(le1==0||le2==0)  return 0;17     if(le1!=le2)  return le1<le2;18     for(int i=0;i<le1;i++)19         if(a[l1+i]!=a[l2+i])  return a[l1+i]<a[l2+i];20     return 0;21 }22 23 //dP[i]=max(j),(T(dP[j-1],j-1)<T(j,i))24 void DP1(){25     for(int i=1;i<=n;i++){26         dP[i]=1;27         for(int j=i;j;j--)28             if(cmp(dP[j-1],j-1,j,i)){29                 dP[i]=j;30                 break;31             }32 //        printf("dP[%d] = %d\n",i,dP[i]);33     }34 }35 36 //Dp[i]=max(j)   (T(i,j)<T(j+1,f[j+1]))37 void DP2(){38     Dp[dP[n]]=n;39     for(int i=dP[n];a[i-1]==0;i--)  Dp[i-1]=n;40     41     for(int i=dP[n]-1;i;i--){42         for(int j=dP[n]-1;j>=i;j--)43             if(cmp(i,j,j+1,Dp[j+1])){44                 Dp[i]=j;45                 break;46             }47 //        printf("Dp[%d] = %d\n",i,Dp[i]);48     }49 }50 51 void print(int l,int r){52     for(int i=l;i<=r;i++)53         putchar(a[i]+0);54 }55 56 void print(){57     print(1,Dp[1]);58     int pos=Dp[1]+1;59     while(pos<=n){60         putchar(,);61         print(pos,Dp[pos]);62         pos=Dp[pos]+1;63     }64 }65 66 int main(){67     cin>>str;  n=str.length();68     for(int i=0;i<n;i++)  a[i+1]=str[i]-0;69     DP1();  DP2();70     print();71     return 0;72 }

 

luoguP1415 拆分数列 [dp]