动态规划4——从两题来看线头DP的基本应用
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
因为准备中考的缘故,已经半年没有没有接触什么新算法了,博客也是六个月没更新过了。差不多要继续了。那么就开始正文的讨论了。
文章目录
- 问题引入
- Problem A 摆花 ($1\text{s},256\text{kB}$)
- 题目大意
- 解题思路
- Problem B Vim
- 题目大意
- 解题思路
- 总结
问题引入
线头DP?
我们见过许多类型的DP,包括序列上的、树或图上的;求最优解的、计数类的……但是这些DP无一例外要遵循一个原则——无后效性。
这个有一点点专业的名次常常在讲DP理论时提到,它简单来说就是前面所做出的的决定不能影响后面的答案。
于是,我们就见到了一些题目:从表面上看,他们在某一处做出的决策会直接影响后面的结果,但实际上,我们可以通过一些转换,让他们也能够用DP来解决。
——让我们先来看第一道题。
Problem A 摆花 ( 1 s , 256 kB 1\text{s},256\text{kB} 1s,256kB)
(不同于NOIP2012普及组的那一题)
题目大意
- 给定 n n n 盆花,可以任意方式排列成一个环。
- 对于第 i i i 盆花,它有四个参数 a i , b i , c i , d i a_i, b_i, c_i, d_i ai,bi,ci,di 以及一个编号 i i i 。
- 假设编号为 i i i 的花所在位置的顺时针下一个位置摆的是编号为 j j j 的花,且 i < j i<j i<j,则花 i i i 的美观度为 a i + b j a_i+b_j ai+bj;
- 假设编号为 i i i 的花所在位置的顺时针下一个位置摆的是编号为 j j j 的花,且 i > j i>j i>j,则花 i i i 的美观度为 c i + d j c_i+d_j ci+dj;
- 方案的美观度等于所有花的美观度之和,求重新排列后最大的美观度。
- n ≤ 2000 , a i , b i , c i , d i ≤ 1 0 5 n\le 2000, a_i, b_i, c_i, d_i \le 10^5 n≤2000,ai,bi,ci,di≤105.
解题思路
就算我事先告诉你这道题可以用DP解决,但是当初看这道题时,我们依然没有一个清晰的思路——当我们算出排到第 i i i盆花的最优解之后,由于后面的编号未知(并且可能因为前面的决策被限定),我们没有办法直接推出它的美观度是多少,更没有办法转移。
那还有办法DP吗?
那我们就先尝试找一找这道题目的特性吧。
既然答案不能被左右两边影响,那么必然就要朝着把不同花各自贡献的那一部分整合到它自身的方向去思考。
比如,当按照顺序排列的时候,记每一朵花的美观度为 g ( i ) g(i) g(i),取 n = 4 n=4 n=4列表:
g ( 1 ) g(1) g(1) | g ( 2 ) g(2) g(2) | g ( 3 ) g(3) g(3) | g ( 4 ) g(4) g(4) |
---|---|---|---|
a 1 + b 2 a_1+b_2 a1+b2 | a 2 + b 3 a_2+b_3 a2+b3 | a 3 + b 4 a_3+b_4 a3+b4 | c 4 + d 1 c_4+d_1 c4+d1 |
∴ ∑ n g ( i ) = a 1 + b 2 + a 2 + b 3 + a 3 + b 4 + c 4 + d 1 = ( a 1 + d 1 ) + ( a 2 + b 2 ) + ( a 3 + b 3 ) + ( b 4 + c 4 ) \begin{matrix} \therefore \displaystyle\sum^{n}g(i)&=&a_1+b_2+a_2+b_3+a_3+b_4+c_4+d_1\\ &=&(a_1+d_1)+(a_2+b_2)+(a_3+b_3)+(b_4+c_4) \end{matrix} ∴∑ng(i)==a1+b2+a2+b3+a3+b4+c4+d1(a1+d1)+(a2+b2)+(a3+b3)+(b4+c4)
我们各个数据整合之后,发现可以各自归到自己那盆花的参数上!
不过上表中有 a + b a+b a+b的,也有 a + d a+d a+d的……到底是那两项相加呢?
这时候,就需要引入线头的概念了!
首先,我们发现这些安排不是随意的。假设依次有相邻的三个数 p , q , r p,q,r p,q,r,如果要求 q q q实际贡献的美观度(也就是上面重新组合过的),那么显然会有这些情况:
g ′ ( q ) = { a q + b q , for p < q < r c q + d q , for p > q > r a q + d q , for q < p , q < r b q + c q , for q > p , q > r g'(q)= \begin{cases} a_q+b_q,\ \text{for}\ p<q<r\\ c_q+d_q,\ \text{for}\ p>q>r\\ a_q+d_q,\ \text{for}\ q<p,q<r\\ b_q+c_q,\ \text{for}\ q>p,q>r\\ \end{cases} g′(q)=⎩⎪⎪⎪⎨⎪⎪⎪⎧aq+bq, for p<q<rcq+dq, for p>q>raq+dq, for q<p,q<rbq+cq, for q>p,q>r
为了使他们连成一个完整的环,如果我们构造了一组是 p < q < r p<q<r p<q<r的排列,那么接下来的一组中 q q q就不能大于 r r r了。
所以,对于每一个点 i i i,如果需要前一个元素小于当前元素,那么我们可以向左伸出一个线头;如果需要后一个元素大于当前元素,那么我们可以向右伸出一个线头……以此类推,如下图所示:
上图中的 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4就分别代表四种情况。
当两个线头碰在一起的时候,他们就可以连成一条完整的线;而所有线构成环时,就代表这种情况成立了,如下图所示:
接下来,我们还需要考虑什么时候有多少个线头。
显而易见地,初始的时候是没有线头的。
当我们从左侧接上一个线头并向右侧延伸时(如情况2和情况3),此时没有接上的线头数量时不变的(左侧消掉一个,但是右侧又引出一个);
当我们从左边接上一个线头时,线头数量会减少一个;
当我们从直接向右边延伸线头时,线头数量会多出一个。
由此,我们就可以得出对应的转移方程式。记 f ( i , j ) f(i,j) f(i,j)表示做到第 i i i盆花,当前有 j j j个线头未处理的最优解。
f ( i , j ) = max { f ( i − 1 , j ) + a i + b i f ( i − 1 , j ) + c i + d i f ( i − 1 , j − 1 ) + a i + d i f ( i − 1 , j + 1 ) + b i + c i f(i,j)=\max \begin{cases} f(i-1,j)+a_i+b_i\\ f(i-1,j)+c_i+d_i\\ f(i-1,j-1)+a_i+d_i\\ f(i-1,j+1)+b_i+c_i\\ \end{cases} f(i,j)=max⎩⎪⎪⎪⎨⎪⎪⎪⎧f(i−1,j)+ai+bif(i−1,j)+ci+dif(i−1,j−1)+ai+dif(i−1,j+1)+bi+ci
由于最差情况下每个点都抛出线头,此时线头数量为 n n n,所以时间复杂度和空间复杂度均为 O ( n 2 ) \text{O}(n^2) O(n2).
答案自然就是做完时线头数为 0 0 0的 f ( n , 0 ) f(n,0) f(n,0),具体细节自己把握咯。
参考代码链接.
Problem B Vim
题目来源:LOJ2687「BalticOI 2013」Vim
题目大意
Victor 正在使用 vim 编辑他的文章,他的文章只包含 abcdefghij
这 个字母,他想把他文章中所有的 e
都删除。Victor 并不是很熟悉 vim,它只懂得下面几个操作:
x
:删除光标所在的字母,光标位置不变。
h
:光标向左移。如果已经是行首就不会移。
f
:后面还会跟一个字母 c
,表示跳到下一个字母 c
的位置。如果不存在那么就不会跳。
悲剧的是 Victor 的键盘上 e
按键突然坏掉了……请问:Victor 最少需要按多少个键才能把所有的 e
删除。
例如,如果当前的文本是 jeff[i]ehadabigidea
,则
x
操作后文本将变为 jeff[e]hadabigidea
h
操作后文本将变为 jef[f]iehadabigidea
f i
操作后文本将变为 jeffiehadab[i]gidea
解题思路
由于e
键损坏,而移动只能向左侧移动,所以必然是要跳到e
后侧的一个字符处往前删最优。那么此时经过一个字母有几种情况:
- 前面的
e
已经可以删去了,因此不需要在这个字符上做停留,直接跳过; - 先到这个字符后面,然后一直挪到它前面,最后再跳到后面;
- 先到后面,再到前面,再到后面,再到前面,最后再到后面;
- ……
后面一次类推不再枚举。然后我们很容易又发现第三种这种跳法远远不如第二种来的划算——除非不需要经过这个字符,否则跳一次就是最优的。于是我们就总结出前两种就是它转移的所有路径。
又因为删去一个e
必然是先一次删除,然后再往左一次回到原来的位置,因此每次删除相当于要按 2 2 2次。这样,答案就是经过一些必过点的最短路径+e
的个数的 2 2 2倍。
那么这些必过点都是什么呢?自然就是e
后的第一个字符。
等一下,这个跳转操作是指定后方的一个字符而并非位置,我们还需要知道自己这个字符到后面需要经过多少个字符,自己的位置在哪里……说着说着这个空间复杂度就不够了……
那我们就再来想一想线头如何放到这道题上吧!
就像上图所示的两种转移(就是前面列举出的两种转移路径),我们可以利用和《摆花》那题中类似的想法,把这些箭头看成是线头,那么只要找出一个完整的路径使得它可以到达最后的点之后(例如设置一个不同于中间所有字符的无穷远点),就可以得到答案。
这次由于不是比大小就可以解决的问题,而是要匹配字符,所以我们可以把字符当做线头进行DP,而不是通过线头数进行DP。
记第一种转移中,当前点为 i i i,跳到的目标点为j
的最佳方案是 f ( i , j ) f(i,j) f(i,j)(j在 i i i之后);第二种转移中,第一次跳到的目标点是j
,折返后第二次再往后跳到k
的最佳方案为 g ( i , j , k ) g(i,j,k) g(i,j,k)。
我们首先考虑跳过这个点的方案。假设当前状态是 f ( i , j ) f(i,j) f(i,j)。
- 首先,有可能上一次是跳到当前点之前,那么为了越过这个点,就需要继续往后调。鉴于每一次
f
操作需要输入两个字符,所以延长这个线头的代价就是 2 2 2; - 当然,也有可能已经跳到后面了,那么就不用管它,不需要代价。
- 以上都是延长第一种线头的方法,实际上延长第二种线头也可以达到相同的目的。不管第一次在哪里折返,只要第二次回到
j
都是合法的转移,并且不需要代价。 - 不过如果当前字符和
j
相同的话, f ( i , j ) f(i,j) f(i,j)应当是到下一个j
的答案,而 g ( i , j , j ) g(i,j,j) g(i,j,j)是到当前这个j
的答案,因此 g ( i , j , j ) g(i,j,j) g(i,j,j)转移到 f ( i , j ) f(i,j) f(i,j)时要付出 2 2 2的代价。
接着考虑折返的方案。假设当前状态为 g ( i , j , k ) g(i,j,k) g(i,j,k)。
- 从 f ( i − 1 , j ) f(i-1,j) f(i−1,j)延长线头:因为要进行折返,折返到前一格代价是 1 1 1,再跳到后面代价是 2 2 2,因此总代价是 3 3 3。
- 同4可知当
k
与 i i i上的字符相同时要多花 2 2 2的代价。 - 剩下的由 g ( i − 1 , j , k ) g(i-1,j,k) g(i−1,j,k)转移的几种情况就比较清晰了。
如果上面文字没有看懂也可以参见一下转移的核心代码:
//for每一个i,对于每一个i的转移如下
for(int j=0;j<MAXS;j++){if(j!=s[i]&&!col[i])//col表示前面一格处是否有一个e需要删除f[i][j]=min(f[i][j],f[i-1][j]);f[i][j]=min(f[i][j],f[i-1][s[i]]+2);if(j!=s[i])f[i][j]=min(f[i][j],g[i-1][s[i]][j]);f[i][j]=min(f[i][j],g[i-1][s[i]][s[i]]+2);for(int k=0;k<MAXS;k++){if(j!=s[i])g[i][j][k]=min(g[i][j][k],f[i-1][j]+3);g[i][j][k]=min(g[i][j][k],f[i-1][s[i]]+5);if(j!=s[i]&&k!=s[i])g[i][j][k]=min(g[i][j][k],g[i-1][j][k]+1);if(j!=s[i])g[i][j][k]=min(g[i][j][k],g[i-1][j][s[i]]+3);if(k!=s[i])g[i][j][k]=min(g[i][j][k],g[i-1][s[i]][k]+3);g[i][j][k]=min(g[i][j][k],g[i-1][s[i]][s[i]]+5);}
}
这样,我们通过匹配前后的每一个字符把线头连接起来,同样做完了这道题目。
参考代码链接.
总结
线头DP终归是一个比较复杂的DP专题,需要对题目有很细致的分析,从而把一些不方便处理的前后关系巧妙转化、连接起来。
DP还得慢慢练。
动态规划4——从两题来看线头DP的基本应用相关推荐
- 什么是 “动态规划” , 用两个经典问题举例。
1.什么是动态规划? 看了很多题解,一般解决者开始就说用DP来解,然后写了嵌套的for循环,不是很容易看懂,但是确实解出来了,我们这次来看下到底什么是动态规划?它有什么特点呢?容我抄一段话: 动态规划 ...
- Leetcode动态规划题解1——两要素和解题步骤
动态规划概述 动态规划,是一种解决最优化问题的方法,在我看来就是一种穷举算法.只不过,这种穷举算法具有"特殊性质".一般的穷举法,就是列出问题所有的可行解,然后通过比较找到最优解, ...
- 【算法】【递归与动态规划模块】两个字符串的公共最长子序列
目录 前言 问题介绍 解决方案 代码编写 java语言版本 c语言版本 c++语言版本 思考感悟 写在最后 前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批 ...
- 《动态规划入门》刷题笔记(更新中)
<动态规划>刷题笔记 1. 斐波那契数 2. 第 N 个泰波那契数 3. 爬楼梯 4. 使用最小花费爬楼梯 5. 打家劫舍 6. 打家劫舍 II 7. 删除并获得点数 8. 跳跃游戏 9. ...
- 【BOI2013】文本编辑器——全面理解线头DP
[BOI2013]文本编辑器 (Standard IO) Time Limits: 2000 ms Memory Limits: 262144 KB Detailed Limits Descripti ...
- 2019年杭电多校第一场 1001题blank(DP)HDU6578
2019年杭电多校第一场 1001题blank(DP)HDU6578 解决思路,开一个DP数组来存储0 1 2 3四个字符最后出现的位置,并且在DP中已经==排好序==. DP开四维,DP[i][j] ...
- 华为OD机试2022.11.04 只记得两题
OD机试两题简单,一题中等:时间来不及只能做简单的两个. 第一题探索地块建立给一块n*m的地块,相当于n*m的二维数组,每个元素的值表示这个小地块的发电量:求在这块地上建立正方形的边长为c的发电站,发 ...
- noip2016 小结(ac两题+学习总结)
NOIP2016考试小结 DAY 1 T1 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业. 有一天, 这些玩具小人把小南的眼镜藏了起来. 小南发现玩具小人们围成了一个圈,它们有的面朝圈内, ...
- cstring查找子字符串_动态规划6:两个字符串的最长连续公共子串
本文和前一篇:动态规划5-两个字符串的最长公共子序列类似,但公共子串必须是连续的,子序列不需要连续 字符串a,长度为m:a[1].a[2].a[3].a[4]....a[m] 字符串b,长度为n:b[ ...
最新文章
- MySQL的系统数据库是_mysql数据库系统
- 一次完整的HTTP事务过程--超详细!
- ubuntu12.10 64位编译Android4.1
- Java 应用性能调优,可视化工具
- jquery div拖动效果示例代码
- leetcode1007. 行相等的最少多米诺旋转(贪心)
- Winform控件WebBrowser与JS脚本交互
- 保留小数点后两位小数
- 数据库系统实训——实验十——事务
- 人工智能为什么这么火?
- mysql报错:ERROR 1045 (28000): Access 解决办法
- 递归;杨辉三角;正则表达式
- 宏基4736ZG更换键盘图解
- Docker Registry搭建私有镜像仓库(干货)http/https
- QModelIndex/Role/Model介紹一
- windows时间与Internet时间不同步
- Electron--快速入门
- 哲理小故事300篇(1—100)
- Springboot 激活指定的配置文件
- 【服务器数据恢复】LINUX误删除、误格式化的数据恢复
热门文章
- time_t tm systemtime 互相转换
- 自己做的ppt放到其他电脑上没有声音
- The hunt(狩猎)影评
- email协议与服务器关系,常见的邮件协议:POP3、IMAP、SMTP之间的区别和联系
- python 传参之后 显示缺失参数_Python中的参数传递问题
- 基础:什么是平均负载
- 山东大学软件学院项目实训-创新实训-山大软院网络攻防靶场实验平台(十四)-任意文件下载漏洞(2)
- java reds 集群_java项目中配置redis-cluster集群的两种方式
- 兼职创业小白日赚三千块的亲身经历分享
- 小学一年级计算机社团计划,一年级2班写字社团活动计划