luoguP1415 拆分数列 [dp]

Posted ZYBGMZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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]的主要内容,如果未能解决你的问题,请参考以下文章

luogu P1415 拆分数列 序列DP

[luoguP2513] [HAOI2009]逆序对数列(DP)

洛谷P1415 拆分数列

luoguP4000 斐波那契数列

[luoguP1962] 斐波那契数列(矩阵快速幂)

[luoguP2626] 斐波那契数列(升级版)(模拟)