动态规划DP -总结

原作者把DP的题目分成了五类:

  1. 不同路径最值合成值
  2. 达到目标的不同方式的种数
  3. 区间合并
  4. 字符串DP
  5. 取舍决策

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


I hope these tips will be helpful
在这里插入图片描述

  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值