zoukankan      html  css  js  c++  java
  • 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 }
    心之所动 且就随缘去吧
  • 相关阅读:
    Watchguard公司内部招聘:C Developer in Linux Requirements
    条件注释判断浏览器<![if !IE]><![if IE]><![if lt IE 6]><![if gte IE 6]>
    js之事件冒泡和事件捕获详细介绍
    javascript:;与javascript:void(0)使用介绍
    IE和FireFox中JS兼容之event .
    Adobe下周将推新补丁和新的更新模式 狼人:
    微软下周二发布11个补丁 修复25个安全漏洞 狼人:
    安全专家担心Adobe没有足够实力来阻止黑客攻击 狼人:
    保证安全 认清五种易被忽视的攻击方式 狼人:
    六成黑客攻击与PDF漏洞有关 远超微软 狼人:
  • 原文地址:https://www.cnblogs.com/iat14/p/10659298.html
Copyright © 2011-2022 走看看