概述
原作者把DP的题目分成了五类:
- 不同路径最值合成值
- 达到目标的不同方式的种数
- 区间合并
- 字符串DP
- 取舍决策
1. 不同路径的最值合成目标值
一般这类问题具有这样的描述:
给定一个target,找到最大(小)的 cost/path/sum 来达到这个target。
解决方法:
在更新当前状态之前,选择所有可能路径里面最大(小)的路径。然后加上当前节点值。
dp[i] = min(dp[i - 1], dp[i - 2], ... , dp[i - k]) + cost[i];
ways 代表了到达dp[i]状态的方案数
for (int i = 1; i <= target; ++i) { // i 不是索引,而是 目标值
for (int j = 0; j < ways.size; ++j) {
if (ways[j] <= i) {
dp[i] = min(dp[i], dp[i - ways[j]]) + cost / path / sum;
}
}
}
return dp[target];
其他相似的问题有:
746.Min Cost Climbing Stairs
for (int i = 2; i <= n; ++i) {
dp[i] = min(dp[i - 1], dp[i - 2]) + (i == n ? 0 : cost[i]);
}
return dp[n];
64.最小路径和
for (int i = 1; i < n; ++i) {
for (int j = 1; j < m; ++j) {
grid[i][j] = min(grid[i - 1][j], grid[i][j - 1])
+ grid[i][j];
}
}
return grid[n - 1][m - 1];
322.换零钱
for (int j = 1; j <= amount; ++j) {
for (int i = 0; i < coins.size(); ++i) {
if (coins[i] <= j) {
dp[j] = min(dp[j], dp[j - coins[i]] + 1);
}
}
}
其他类似问题:
120
174
221
279
322
474
650
931
983
2 达到目标的不同方式的个数
问题描述:给一个目标,问到达目标的不同方式的个数
一般会给出target,让你求合成它的方法数,比如 : 377.组合总数IV
这里和类型1的区别是,方法1求的最值
,所以在方程里要比较新dp[i]和之前的dp[i]大小
类型2这里是求所有种类,所以dp[i]直接累加
两类区别可以看377 和 322 的区别,很典型。
另外,还要分清是排列问题还是组合问题;即:答案包括的序列有没有顺序要求
排列:如爬楼梯和377这种,target(amount)的循环放外面
组合:如518零钱兑换II , target(amount)循环放里面,nums循环放外面;
具体见:零钱兑换II和爬楼梯问题到底有什么不同?
以及dp文档里 377 - 518 题的总结
解题方法: 累加所有可以到达当前状态的 状态和
dp[i] = dp[i - 1] + dp[i - 2] , ... , + dp[i - k];
比如青蛙跳台阶: k=2 dp[i] = dp[i - 1] + dp[i - 2]
for (int i = 1; i <= target; ++i) {
for (int j = 0; j < ways.size; ++j) {
if (ways[j] <= i) {
dp[i] += dp[i - ways[j]];
}
}
}
return dp[target];
377.组合总和IV
public int combinationSum4(int[] nums, int target) {
if (nums.length == 0) return 0;
int[] dp = new int[target + 1];
dp[0] = 1; // 代表nums[i]本身就等于target
for (int i = 1; i <= target; ++i) {
for (int j = 0; j < nums.length; ++j) {
if (i >= nums[j])
dp[i] += dp[i - nums[j]];
}
}
return dp[target];
}
70.爬楼梯
for (int i = 2; i <= n; ++i)
dp[i] = dp[i - 1] + dp[i - 2] ;
上面模板里面 k = ways.size = 2;
代表当前楼梯可以从上一层跳上来,也可以从上上层跳上来;
一共两种方式到达当前状态dp[i]
62 Unique Paths
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
}
}
1155 Number of Dice Rolls With Target Sum
其他类似题:
63
377. 组合总和 Ⅳ
416
494
576
673
688
801
3 Merging Intervals 区间合并
问题描述:
给定一组数字,考虑当前 i 以及从左侧和右侧可获得的最佳值,找到问题的最佳解决方案。(类似于戳气球那种题)
解题方法:
// 从 i 到 j
dp[i][j] = dp[i][k] + result[k] + dp[k + 1][j]
Get the best from the left and right sides and add a solution for the current position
for (int l = 1; l < n; ++l) {
for (int i = 0; i < n - l; ++i) }
int j = i + l;
for (int k = i; k < j; ++k) {
dp[i][j] = max(dp[i][j],
dp[i][k] + result[k] + dp[k + 1][j]);
}
}
}
类似问题:
1130.Minimum Cost Tree From Leaf Values
for (int l = 1; l < n; ++l) {
for (int i = 0; i < n - l; ++i) {
int j = i + 1;
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; ++k) {
dp[i][j] = min(dp[i][j],
dp[i][k] + dp[k + 1][j]; + maxs[i][k] * maxs[k + 1][j](;
}
}
}
其他类似题:
96
312
375
546
1000
4 字符串上DP
此情况下不同的题的问题陈述差别挺大,但大多数情况下,题目会给两个字符串,这些字符串的长度并不大
问题描述:
给两个字符串s1,s2 返回 一些结果
解决方法:
大多数解法都要 O(n*n) 的复杂度。
i : s1 的索引
j : s2 的索引
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (s1[i - 1] == s2[j - 1])
dp[i][j] = /*code*/
else
dp[i][j] = /*code*/
}
}
如果只给出了一个字符串,解法稍有不同:
for (int l = 1; l < n; ++l) {
for (int i = 0; j < n - l; ++i) {
int j = i + l;
if (s[i] == s[j])
dp[i][j] = /*code*/
else
dp[i][j] = /*code*/
}
}
类似题目:
1143.Longest Common Subsequence
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (text1[i - 1] == text2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
647.Palindromic Substrings
for (int l = 1; l < n; ++l) {
for (int i = 0; j < n - l; ++i) {
int j = i + l;
if (s[i] == s[j] && dp[i + 1][j - 1] == j - i + 1)
dp[i][j] = dp[i + 1][j - 1] + 2;
else
dp[i][j] = 0;
}
}
其他类似题:
5
72
115
516
712
1092
5 取舍决定(当前元素取或不取)
决策问题: 决定是否使用当前状态。 需要在当前状态下做出决定。
问题描述:
给定一组值,找到答案,并提供 选择 或 忽略 当前值的选项
典型的例子就算打家劫舍(相邻的房子不能一起偷,要决策是偷上一个房子,还是偷当前房子)
解决方法:
If you decide to choose the current value use the previous result where the value was ignored;
vice-versa, if you decide to ignore the current value use previous result where value was used.
如果你选择使用前一个的状态结果,那么当前值不会用;
相反,如果你选择使用当前值,那么上一次结果不能用。
典型的例子就算打家劫舍(相邻的房子不能一起偷)
i : 遍历的数组
j : 选择要忽略的值
for (int i = 1; i < n; ++i) {
for (int j = 1; j <= k; ++j) {
dp[i][j] = max(dp[i][j],
dp[i - 1][j] + arr[i]
dp[i - 1][j - 1]);
dp[i][j - 1] = max(dp[i][j - 1],
dp[i - 1][j - 1] + arr[i]
arr[i]);
}
}
198 打家劫舍
for (int i = 1; i < n; ++i) {
dp[i][1] = max(dp[i - 1][0] + nums[i], dp[i - 1]);
dp[i][0] = dp[i - 1][1];
}
其他类似题:
121
123
188
309
714
最后
以上就是轻松草丛为你收集整理的动态规划DP -总结的全部内容,希望文章能够帮你解决动态规划DP -总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复