动态规划 总结

一、动态规划最精辟的总结

首先, 动态既不是什么动态,也不是什么规划,存粹就是对递归的优化;

其次,动态规划依赖于两个基本情况:边界条件和递推关系,边界条件就是最简单的情况,所谓递推关系就是如果你已经知道这么多,再多给你一个,你怎么得到。

二、动态规划的经典模型

       1、线性模型

       2、区间模型

       3、背包模型

       4、状态压缩模型

       5、树状模型


https://blog.csdn.net/y389224734/article/details/81269245?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control



关于搜索过程是否需要保存所有状态,我觉得不在于算法(DFS或BFS),而在于实现方式(递归或迭代)。

    在多叉树遍历中,递归和迭代的计算过程并不完全一样。递归的形式,在某个“节点”中,递归调用前和后都可以执行自定义逻辑;而用显式栈迭代的形式,在某个“节点”中只能先做自定义逻辑,再将所有孩子(子状态)依次入栈,而做不了“在递归调用后执行逻辑”这个过程,所以每个节点都需要保存当时走到这儿时的状态,可以用多个平行栈,或者用自定义类的多个属性,就像您下边回复里定义的Ceil类,这其实也是模拟了递归时的系统栈帧,栈帧中保存的方法入参和显式栈中保存的多个变量值或类的多个属性值就表征了此时的局面。

    而BFS一般无法用递归的形式,在用队列迭代的过程中,每个结点也是一旦走过就永远错过了,所以也需要自己保留自己的状态。

顺便再说下我对动规的理解,动规中常提的“状态”实际就是回溯中的“状态”去掉路径和选择列表。回溯中随着不断的决策也是会到达一个个子局面,因为回溯是穷举,所以他要记录我是怎么到达这个子局面的;而动规是:我们先分析下可能会到达哪些子局面,然后先把子局面的解求出来,再根据当前状态的选择列表,将每个可做出的选择会到达的子局面的解通过某种关系组合,这个关系就是状态转移方程,这个组合形式就叫最优子结构

https://leetcode.com/problems/maximal-square/ 这个题就是对这个动归最好的理解;


三 关于怎么定义状态

所谓「状态」,就是指当前的任务进行到哪个阶段了,可以用变量来表示,怎么定义状态有的时候需要一定技巧,这道题不难。这里分别定义两个水壶为 A 和 B,定义有序整数对 (a, b) 表示当前 A 和 B 两个水壶的水量,它就是一个状态。


四 动态规划我自己的定义

最优子结构就是能产生链式反应的结构,比如跳台阶,然后可以着手暴力解,但这个暴力解不是通过递归来实现的,暴力解之后你就要看是否右重叠子结构,然后就可以着手优化了;

参考了:https://github.com/labuladong/fucking-algorithm/blob/master/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%B3%BB%E5%88%97/%E6%9C%80%E4%BC%98%E5%AD%90%E7%BB%93%E6%9E%84.md


五 动态规划的分类

1)暴力思路(回溯) dfs的递归本身就是逆推的 就是所谓的自顶向下,然后就归

174. Dungeon Game

回溯公式: dfs(0, 0, rowLen, colLen, dungeon)+1;

退出条件:达到最后一个且最后一个的值>=0

继续递归: int rightMin=dfs(rowIndex, colIndex+1, rowSize, colSize, dungeon);

结果: int needMin = Math.min(rightMin, downMin)+dungeon[rowIndex][colIndex];

22. Generate Parentheses

倒序

sb.append('(');  //左边配置一个(,必然左边要去回溯;

dfs(res, left-1, right, sb);  //sb用来创建字符串 所以要用StringBuilder

sb.deleteCharAt(sb.length-1);

45. Jump Game II

far_max = Math.max(far_max, i+nums[i]);  //i+nums[i]是这题的关键

55. Jump Game

far_max = Math.max(far_max, i+nums[i]);

return fax_max>=n-1;

53. Maximum Subarray

sum = Math.max(nums[i], sum+nums[i]);  //贪心

77. Combinations

res.add(new LinkedList<Integer>(track));

关键:track.add(i); backtrack(track, i+1, n, k); track.removeLast();

152. Maximum Product Subarray

这题跟53题是一样的,多出来的一点是:if(nums[i]<0) 即要对调;我觉得刷题最耗时间的是理解,找到最容易理解的方式那就是最节约时间的方式,也是最高效的,因为算法都是套路;

minValue = Math.min(nums[i], minValue*nums[i]);




2)记忆化搜索

174. Dungeon Game

int[][] memo = new int[m][n];

Math.min(dp(i, j+1, dungeon), dp(i+1, j, dungeon))+dungeon[rowIndex][colIndex];

10. Regular Expression Matching

res = isMatch(s, p.substring(2)) || isMatch(s.substring(1),p);

res = isMatch(s.substring(1),p.substring(1));



3)dp数组 - 递推,自底向上

174. Dungeon Game

//倒序:从(i,j)出发,到达终点需要最少的血量

Math.min(dp[i][j+1], dp[i+1][j])-dungeon[rowIndex][colIndex];

44. Wildcard Matching

if(s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='?')  //如果第1个字母相等 或 第一个字母是?

dp[i][j] = dp[i-1][j-1];

 if(p.charAt(j-1)=='*')  //只要pattern的第一个字母是* 即可以去掉i的第一个字母,也可以去掉j的第一个字母

dp[i][j] = dp[i-1][j]||dp[i][j-1];  //往上、往下都没问题

62. Unique Paths

到dp[m-1][n-1]这个坐标有多少种走法?

dp[i][j] = dp[i][j+1]+dp[i+1][j];

63. Unique Paths II

到dp[m-1][n-1]这个坐标有多少种走法?虽然有障碍

if(obstacleGrid[i][j]==1) dp[i][j] = 0;  //此路不通   

else if(i==0 && j==0) dp[i][j] = 1;

else if(i==0 && j>0) dp[i][j] = dp[i][j-1];

else if(i>0 && j==0) dp[i][j] = dp[i-1][j];

else dp[i][j] = dp[i-1][j]+dp[i][j-1];

64. Minimum Path Sum

if(i==0 && j==0) dp[i][j] = grid[i][j];  //0 0位置实际上是第一个位置

else if(i>0 && j==0) dp[i][j] = grid[i][j]+dp[i-1][j];

else if(i==0 && j>0) dp[i][j] = grid[i][j]+dp[i][j-1];

else dp[i][j] = grid[i][j]+Math.min(dp[i][j-1],dp[i-1][j]);  //这是关键   

70. Climbing Stairs

dp的定义:也是有多少种跳法;

dp[1]=1;

dp[2]=2;

dp[i]=dp[i-1]+dp[i-2];

72. Edit Distance

dp的定义:dp[m][n]是指需要多少步;

dp[i][j] = dp[i-1][j-1];

dp[i][j] = getMin(dp[i][j-1]+1, dp[i-1][j]+1, dp[i-1][j-1]+1);

85. Maximal Rectangle

dp[m][n] 应该标记的是高度,而count则是宽度;

关键的转移方程是:

if(matrix[r][c]=='1') dp[r][c]=dp[r-1][c]+1;  //取决于下一排的情况

//n是宽

for(int k=c+1; kdp[r][c] 则count++

for(int k=c-1; k>=0; k--) if(dp[r][k] < dp[r][c]) break; count++;

最终的结果就是:Math.max(res, count*dp[r][c]);

91. Decode Ways

dp[]的定义:dp[n]是最终的结果;

dp[0]=1  //0个字符有1个拆解方法  //由于题目存在前导零,而前导零属于无效 item

if(s.charAt(i-1)!='0') dp[i] += dp[i-1];  //dp[i] = dp[i]+dp[i-1];

if(i>1 && s.charAt(i-2)!='0' && (s.charAt(i-2)-'0')*10+(s.charAt(i-1)-'0')<=26) dp[i] += dp[i-2];  //dp[i] = dp[i]+dp[i-2];

96. Unique Binary Search Trees

dp[i] += dp[j-1]*dp[i-j];

115. Distinct Subsequences

i最后一个字母不参与: dp[i][j] = dp[i-1][j];  //不是+=

最后一个字母参与:dp[i][j] += dp[i-1][j-1];

120. Triangle

n+1  //因为右i+1, j+1, 所以还是从0到n

//从底到顶,从左到右

dp[i][j] = Math.min(dp[i+1][j], dp[i+1][j+1])+triangle.get(i).get(j);

return dp[0][0];

121. Best Time to Buy and Sell Stock

122. Best Time to Buy and Sell Stock II

123. Best Time to Buy and Sell Stock III

188. Best Time to Buy and Sell Stock IV

//不能同一天买卖

buy = Math.max(buy, -prices[i]); //-prices[i]就是负值,以昨天的卖为基准值 昨天有的

sell = Math.max(sell, buy+prices[i]);

//可以当天买卖

buy[i] = Math.max(buy[i-1], sell[i-1]-prices[i]); //与前一天比,昨天卖sell[i-1],今天买-prices[i]

sell[i] = Math.max(sell[i-1], buy[i-1]+prices[i]);

//只有两次交易

buy1 = Math.max(buy1, -prices[i]);

sell1 = Math.max(sell1, buy1+prices[i]);

buy2 = Math.max(buy2, sell1-prices[i]);  //上次卖

sell2 = Math.max(sell2, buy2+prices[i]);

//可以有K次交易

sell[i] = Math.max(sell[i], buy[i]+p);

buy[i] = Math.max(buy[i], sell[i-1]-p);

buy[j] = Math.max(buy[j],sell[j-1]-prices[i]);  //前一天卖的

sell[j] = Math.max(sell[j],buy[j]+prices[i]);

139. Word Break

关键是从j到i的判断,然后就是HashSet是关键点;

if (dp[j]&&wordDicSet.contains(substring(j, i))) dp[i]=true;  //dp[j]是一个小的范围,动态规划就是由小及大;

152. Maximum Product Subarray

这个是相乘积,这个怎么由小及大呢?

dp[i] = Math.max(dp[i]*dp[i-1]);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容