谈谈我近一个半月的dp练习
前请提示:https://www.cnblogs.com/caiyishuai/p/9047991.html 配合这篇文章食用风味更佳哦!
首先十分感谢henry_y提供的50道dp练习,链接在这—>https://www.cnblogs.com/henry-1202/p/9211398.html
虽然现在只做了30多道题,剩下的题目还没写,想着以后留着复习来用,但是我对dp的理解比以前高出了不少,现在我来说说对dp的想法吧。
目录
一.动态规划的本质,以及它的核心难点:如何判断这题用dp来解
二. 关于我做到的关于dp以外的技巧
一.动态规划的本质,以及它的核心难点:如何判断这题用dp来解
其实说白了,用到动态规划的题目肯定要满足无后效性,最优子问题这两个关键,但是有些十分有趣的现象,就是如果我们看到一道题,别人没有跟你讲这道题正解是用dp做的,你横看竖看这题也不像dp,那怎么办呢?(这里给出一个实例,有兴趣的可以看一下这题 回文字串 https://www.luogu.org/problem/P1435),那么这些什么无后效性的概念对于我们现在来讲就是废话,因为我们是真的看不出来这题竟然能用dp做,那么现在我给出我自己对这些题目的浅薄理解。
首先,要用dp解题应要立于题目本身,题目要求有求“最大”,“最小”,“最优”等字眼时,应该要想到这题可能要用到dp;
其次,没有头绪时不用慌,观察题目给的样例数据,看看数据都是关于什么,比如背包问题,数据常见的给出有关于价值、容量、重量等等关键元素,这时候就要想一下如果我用dp来写这题,该怎么写。
举个例子,我用dp的思路,常见的就是用F[ i ][ j ]这种二维形式来推出答案,这时候我们应该要想, i 能表示什么,j能表示什么,假设出一种可能后,看看能不能、可不可以写出动态转移方程。
这里我给出一个实例,刚刚那题回文字串,看了题目一脸疑惑,第一反应肯定是暴力,硬搜出答案出来,但是肯定会炸裂,那该怎么办?
我们来看它求什么,“最少插入字符数”,你看!最少这个字眼出现了,直接把解题思路集中在贪心、搜索(这个万不得已的时候才用,因为剪枝技术不行的话,99%超时)、dp等找最优解的算法上(欢迎大家来补充,因为我的知识有限,只了解这些。。。),那么如何来区分是贪心还是dp来解题呢?这里我不再进行解答,开头给出的文章有详细的解析。
我们把目光回到那道题目上面,怎么做?我们可以看到样例有大写字母,小写字母,数字等不同的字符,注意到了吗?不同的字符—>种类(找到第一个元素)每个字符占着不同的位置 —>位置(找到第二个元素) 字串的长度—>长度(找到第三个元素)就这么多吧。
我们再来看看题目的要求,回文字串,题目样例是Ab3bd,如果我们插入它的倒序db3bA,即Ab3bddb3bA,不就是个回文字串吗?而且这么做的话,最后字串个数肯定是偶数,因为倒过来加上去字符个数就是2n,那么我们还可以舍弃最后一个字母来加上去,意思是样例是Ab3bd,回文字串把它变成Ab3bdb3bA,把d给删掉一个,不也是个回文字串吗?那么我们就很容易得出结论 加上去的字符数最多是 原字串长度减去1,(这里先看到下面 1 长度 这个元素)但是有个问题,我原本就有对称趋势的字符,如果这么加,不就浪费了吗?就像样例中的b3b,本已回文,何必再加(看到下面 2 种类 这个元素),那么如何判断这些可以构成回文的部分呢?根据刚刚的思路,倒过来后这些本来已经回文的子串还是回文的,那么不就有点像LCS了吗?我们只需找出那些倒过来还是可以弄成回文的子串(就算是断断续续的也无妨),不就问题迎刃而解?(看到下面 3 位置 这个元素)
现在我们要选一下哪些有用,而且要把它们应用在动态转移方程上面
1 长度 找到了长度这个元素!那怎么可以把它用到方程里面,对于样例而言,长度为5,那么我们只能从5开始,从中间位置开始向两边枚举,但是没有dp的思想在里面啊,否决!
2 种类 回文串的种类个数是串的长度/2(单数串长度还要+1) 我们是否能这么设置?f[ i ][ j ]表示前i 个字符一共有J种种类而需要插入的个数构成的回文串这样子的呢?我们也易看到得出的结果跟我下一个需要判断的阶段好像没啥关系,因为我还是要扫一遍,这不就是搜索了吗?否决!
3 位置 刚刚分析过了,如果我倒过来的串的位置与原串的同一位置的字符一样,那不就看出来这是有对称趋势的字符?配合LCS的思路,最后求出来的就是所以有对称趋势的字符的个数的值,那么我们要求出最少插入的值不就是原串长度-求出的值不就是答案呢吗?为什么呢?把那些没有对称趋势的字符复制在它的“对面”,这个字符就与自己的复制体就对称了,不就从而弄出来答案就是剩下的没有对称趋势的字符的个数吗?而剩下的没有对称趋势的字符不就等于原串长度-求出的值。
以上就是我对其中一题的理解,即是当我没有任何思路时,会去想的方法。 绝不是我事先知道这题是dp才往那方向死命想的
总结一下以上的想法,我就是把题目里面能想到的元素都写出来,再根据题目要求一个个去尝试,因此我在做这些题目时,开了一维的数组,发现好像不行,那就开二维,二维不行就上三维,直到把我能想到的且最终有用的元素都用上。
二. 关于我做到的关于dp以外的技巧
1.尼克的任务(https://www.luogu.org/problemnew/solution/P1280)
这道题可谓是惊天水题,太水了,水的我都溺死了,一开始我一股脑的用常规dp思路,所谓常规就是i=1~n;j=1~n;F[ i , j ]=.......,结果发现连样例都过不了,一看题解,正解要倒序......。这题我怨气这么多一是因为这是50道dp里面的第3道,说是入门级别的难度我实属是不敢相信,二是因为我在找题解,看正解为什么应该要用倒序时,发现了一些dalao,他们写题解都是怎样的呢?“唔~普及组水题”“这题显然是....”等等,我很想说一句,但凡你们认真一点,照顾照顾一下我这些菜鸡,写题解时多一点解释,少一点显然,我也不至于对dp一直有种畏惧的心理。额,这些都是题外话,回到题目,这题可能是我见过的第一道要用倒序dp来做的题目(除去背包问题),算是长见识了。最后说一句,这题一点都不水!
2.LIS的n log n 做法(50道题目里面比较多用到)
这个做法我一开始是一脸懵逼的,最后理解了才发现,其实这种做法不仅仅能适用于这里,这更算是一种思想,一种最优化的思想,我觉得有点难解释,我把这种做法比喻成排队吧,如果有人比现在这个队列里面最高的人还要高,就把他放在最前面,如果不够最高那个人高,就在队列里面找到所有比他高的人里面最矮的那一个,然后把它替换,这样有利于把后面的人给加进来,为什么呢?如果一个人170,他想要加入队伍,现在这个队伍里面最高的人是175,第二高的人是168,这时候我把175的换走,人数不变,此时最高的是170,便于以后想要加进来的171、172、173、174的人排到最前面,队伍人数增多,而不是让他们走人。
3.对dp出来一遍的数组再进行dp一遍(https://www.luogu.org/problem/P1108)
长见识了,我现在都云里雾里的对这题
4.dp与离散化的结合(https://www.luogu.org/problem/UVA10635、https://www.luogu.org/problem/P1439)
这几题以后一定要重新做一遍,对于一些有两个排序标准的题目很有帮助,而且我初步了解到了离散化数据的魅力,其实这个理解起来也不难,就是对于随便一串东西,把它做成一个映射,变成有排列顺序的数字1 2 3 4 等等,做完题输出的时候再反向映射一遍,巧妙吧,我特别想问谁发明的,真的牛啤。
4.Dilworth定理的意思:最少的下降序列个数就等于整个序列最长上升子序列的长度 (有些东西记住就好,不要太在意,要不然头顶会凉)
5.断链成环(https://www.luogu.org/problem/P1063)
这个我要重点提一下,因为我对环的知识还没有很深,所以做题常常碰壁,这题也不例外,那这题怎么说呢?这题让我懂得了一个道理,我设出来的东西可以不用,我可以只是为了我的代码简洁,循环起来不用写那么多限制条件,最后输出的时候输出我要求的东西就行。回归正题吧,环的处理有两种,可以选择(i+1)%n的方式,但也可以将n个元素复制一遍,变成2*n个元素,简化代码,我刚刚说的道理就是从2*n个元素这种方法得出来的。总而言之,我之后要做多一点关于环的题目吧。
6.与二分的结合(https://www.luogu.org/problem/P3957)
虽然核心dp代码里面没有用到二分优化什么的,纯属算是题目要求,但是我想说一句,这是2017普及组的题目???相对而言算难的吧.......
7.单调队列优化(https://www.luogu.org/problem/P3089)
又是长见识的时间,单调队列我就不说了,现在我还只会手写队列,等到会用stl库再来写写这题吧。其实做完这题的时候我思考了这样一个问题,既然我dp都在找以前的最优值,那么什么时候可以用类似的算法,把以前的最优都处理一下,直接省掉一重循环(思考ing......)
8.DAG上的dp(https://www.luogu.org/problem/P2883)
一开始这题我连题目都看不懂的,看了题解还算是懂了那么一点(对于一个高考语文分数107即三年巅峰的人来讲,这种题目描述实属是难顶),其实这题我更想把它叫为递推,因为对于每个点直接加上连接点的值就好了,说是dp也无妨吧。