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 }

原文地址:https://www.cnblogs.com/iat14/p/10659298.html

时间: 2024-10-01 03:37:40

luogu P1415 拆分数列 序列DP的相关文章

P1415 拆分数列 DP

传送门: 题意: 将一个数字串分成许多不同的小串,使得这些小串代表的数字严格递增,要求最后一个数字尽可能地小. 然后满足字典序尽可能大. 思路: 由于最后一个数字要尽可能地小,所以先处理出每个数的L[i], 即第i位最少要和L[i]这个位子的值组合才能满足要求.求出L[i]后,固定好最后的数的大小. 因为要求前面的数尽可能地大,所以,开始计算R[i],表示第i位最远能和哪个位子的值组合才能满足要求. 注意最后一块的前导0要带上. #include <algorithm> #include &l

luoguP1415 拆分数列 [dp]

题目描述 给出一列数字,需要你添加任意多个逗号将其拆成若干个严格递增的数.如果有多组解,则输出使得最后一个数最小的同时,字典序最大的解(即先要满足最后一个数最小:如果有多组解,则使得第一个数尽量大:如果仍有多组解,则使得第二个数尽量大,依次类推……). 输入输出格式 输入格式: 共一行,为初始的数字. 输出格式: 共一行,为拆分之后的数列.每个数之间用逗号分隔.行尾无逗号. 输入输出样例 输入样例#1: [1] 3456 [2] 3546 [3] 3526 [4] 0001 [5] 100000

【学习】序列DP

做了也有一段时间的序列DP了,发现了一些规律 如果有两个字符串,一般来说,f[i][j]表示S串到第i位,T串到第j位. 如果lenS==lenT,可能可以优化到1维. 如果只有1个序列的话,一般来说f[i]表示到第i位的状态. 有一些特殊的东西:最长回文子序列是把原串倒过来然后做一遍最长公共子序列,检查一下奇偶性×2即可. 然后呢还有最长回文子串有个manacher算法来着改天要去看看. BZOJ上的题好像只做了一道呀= =好像是带计数的数列DP呀,用容斥原理搞一下就好咯. 感觉自己还是很弱还

Fibonacci斐波拉契数列----------动态规划DP

n==10 20 30 40 50 46 体验一下,感受一下,运行时间 #include <stdio.h>int fib(int n){ if (n<=1)     return 1; else            return fib(n-1)+fib(n-2); }int main( ){ int n; scanf("%d",&n); printf("%d\n" ,fib(n) );} 先 n==10 20 30 40 50 46

序列DP(输出有要求)

DP Time Limit:10000MS     Memory Limit:165888KB     64bit IO Format:%lld & %llu Submit Status Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax2 < … < axm).那么就称P为S的一个上升序列.如果有多个P满足条件,那么我们想求字典

2014 Super Training #10 D 花生的序列 --DP

原题: FZU 2170 http://acm.fzu.edu.cn/problem.php?pid=2170 这题确实是当时没读懂题目,连样例都没想通,所以没做了,所以还是感觉这样散漫的做不好,有些题目明明很简单,却因为没看懂而放弃了,甚至去玩了,这样达不到太大的效果. 解法: 定义: dp[i][j]:前i个字母中有j个是属于第一个序列的标号方案种数. 则当遇到'B'时,因为要满足WB依次间歇出现,所以前面属于第一个序列的个数应该为奇数,即j&1时转移.当属于第二个序列的个数为奇数时((i-

FOJ 2170 花生的序列 dp

marketplace是Eclipse发布的一个类似AppStore一样的插件和应用平台,可以从中安装Eclipse插件等.有些旧版本的Eclipse没有安装marketpalace. 一.   marketplace的安装 打开 Eclipse,菜单栏 Help -- Install New Software,弹出 install 窗口,在 Work with 中输入 http://download.eclipse.org/releases/indigo,等待获取插件内容.然后找到 然后 Ne

2017级算法模拟上机准备篇(序列DP 进阶_1)

进阶版的序列DP 从一道题的优化开始 ModricWang的序列问题 题目描述:给定一个序列,求出这个序列中的最长上升子序列的长度. 这道题的本质还是求解一个最长上升子序列的问题 相对与之前提到过的O(n^2)的算法 我们可以重新整理思路 用O(nlogn)的思路来写,用贪心和二分优化之前的算法 我们设置新的DP数组//dp[i]代表的是当前长度为i的上升子序列的末尾元素的大小 状态转移方程为如果dp[len] < ar[i] 那么就将数ar[i]加到dp数组尾部. 反之,说明可以继续优化,显然

Neko and Aki&#39;s Prank CodeForces - 1152D (括号序列,dp)

大意: 将所有长度为2*n的合法括号序列建成一颗trie树, 求trie树上选出一个最大不相交的边集, 输出边集大小. 最大边集数一定不超过奇数层结点数. 这个上界可以通过从底层贪心达到, 所以就转化为求奇数层结点数. 然后就dp求出前$i$为'('比')'多j个的方案数, 奇数层且合法的时候统计一下贡献即可. #include <iostream> #include <iostream> #include <algorithm> #include <cstdio