luogu P1415 拆分数列 序列DP

Posted iat14

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P1415 拆分数列 序列DP相关的知识,希望对你有一定的参考价值。

做起来不太难,但是很难想的一道题。

分两个步骤,第一个步骤是先求出,最后一个数字是多少。

我们考虑d[i]表示,[1,i]最后一个数字最小情况下,最后一个数字的开始位置。 

那转移方程很显然,d[i] = j(满足s[d[j - 1],j - 1]<s[j,i],且j距离i最近,这样子最小)。

这样我们就求除了最后一个数字是多少。

第二步,我们类似的从后推一遍。

用f[i]表示,[i,n]第一个数字最大情况下,第一个数字的结束位置。 

转移方程依旧显然,f[i] = j(满足s[i,j] < s[j + 1,f[j + 1]]且离i最远的j,这样子最大)。

然后我们最后顺着f[i]输出即可。

但是有一些问题,就是我们第一步骤求出的最后一个数字,可能存在前导0。

比如100300这个例子,300作为最后一个数字不应该吞并前导零。

而1234050这个例子,50作为最后一个数字应该吞并前导零。

那这个如何处理呢,我们考虑在进行第二次dp前,让最后一个数字的所有前导零的f值全部为n。然后我们从非零的位置开始DP。可以理解成,我先让所有前导零与最后一个数字合并,如果前面的某个数字x发现合并了后面这些前导零更优的时候,我允许他把它合并掉,即f[x]指向某些前导零。有种有需要,则自取的感觉。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring> 
 4 using namespace std;
 5 char s[510];
 6 int d[510],f[510];
 7 int len;
 8 bool cmp(int x1,int y1,int x2,int y2)
 9 {//若[x2,y2] > [x1,y1]则返回true 
10     while (s[x1] == 0 && x1 != y1)
11         x1++;
12     while (s[x2] == 0 && x2 != y2)
13         x2++;
14     if (y2 - x2 > y1 - x1)
15         return true;
16     else if (y2 - x2 < y1 - x1)
17         return false;
18     for (int i = x1;i <= y1;i++)
19         if (s[i] < s[x2 + i - x1])
20             return true;
21         else if (s[i] > s[x2 + i - x1])
22             return false;
23     return false;
24 }
25 void print(int x,int y)
26 {
27     for (int i = x;i <= y;i++)
28         printf("%d",s[i]);
29     if (y != len)
30         printf(",");
31 }
32 bool check(int x,int y)
33 {
34     for (int i = x;i <= y;i++)
35         if (s[i] != 0)
36             return false;
37     return true;
38 }
39 int main()
40 {
41     scanf("%s",s + 1);
42     len = strlen(s + 1);
43     for (int i = 1;i <= len;i++)
44         s[i] -= 0;
45     //d[i]表示,[1,i]最后一个数字最小情况下,最后一个数字的开始位置。 
46     d[1] = 1;
47     for (int i = 2;i <= len;i++)
48         for (int j = i;j >= 1;j--)
49             if (cmp(d[j - 1],j - 1,j,i) == true)
50             {
51                 d[i] = j;
52                 break;
53             }
54     //f[i]表示,[i,n]第一个数字最大情况下,第一个数字的结束位置。 
55     int t=d[len];
56     do
57     {
58         f[t]=len;
59         t--;
60     }while ((t>=1)&&(s[t]==0));
61     f[d[len]] = len;
62     for (int i = t;i >= 1;i--)
63         for (int j = d[len] - 1;j >= i;j--)
64             if (cmp(i,j,j + 1,f[j + 1]) == true)
65             {
66                 f[i] = j;
67                 break;
68             }
69     for (int i = 1;i <= len;i++)
70     {
71         print(i,f[i]);
72         i = f[i];
73     }
74     return 0;
75 }

 

以上是关于luogu P1415 拆分数列 序列DP的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1415 拆分数列

Luogu P4933 大师dpBy cellur925

[luogu2513 HAOI2009] 逆序对数列 (计数dp)

luoguP1415 拆分数列 [dp]

Luogu P2023 [AHOI2009]维护序列

Luogu P1356 数列的整数性题解