文章目录
1. 小偷背包问题
偷商品的一部分, 不能用dp, 只能用贪心(dp, 要么考虑拿走整件商品, 要么不拿, 偷走的商品不可分割性)
2. 动态规划只能解决的问题: 相互离散且独立
3. 实例(代替递归, 把结果记录下来, )
参考文献
题目:
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
public int getAns(int n){
int[] dp = new int[10];
dp[0] = 1;
dp[1] = 1;
for (int i=2; i<=n; i++ ){
for (int j=0; j<i; j++){ //root的左子树的节点数目
dp[i] += dp[j] * dp[i-1-j];
}
}
return dp[n];
}
- 递归解法
class Solution:
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
if n == 0 or n == 1:
return 1
res = 0
for j in range(1, n + 1):
res += self.numTrees(j-1) * self.numTrees(n-j)
return res
4. 动态规划不如递归的应用场景
5 leetcode 486
证明过程
对于奇数个数字的数组,利用动态规划(dynamic programming)计算。 首先证明最优子结构性质。对于数组[1…n]中的子数组[i…j],假设玩家1在子数组[i…j]中的拿法是最优的,即拿的分数比玩家2多出最多。假设玩家1拿了i,则[i+1…j]中玩家1拿的方法也一定是最优的。利用反证法证明:如果玩家1在[i+1…j]中有更优的拿法,即玩家1在[i+1…j]可以拿到更多的分数,则玩家在[i…j]中拿到的分数就会比假设的最优拿法拿到的分数更多,显然矛盾。如果玩家1拿了j,同理可证矛盾。 所以当前问题的最优解包含的子问题的解一定也是子问题的最优解。
分析过程
对于只有一个数字的子数组,即i=j,dp[i][i] = num[i],因为玩家1先手拿了这一个分数,玩家2就没得拿了,所以是最优拿法。 对于两个数字的子数组,即j-i=1,dp[i][j]=abs(num[i]-num[j]),玩家1先手拿两个数中大的一个,所以玩家1一定比玩家2多两个数字差的绝对值,为最优拿法。 对于j-i>1的子数组,如果玩家1先手拿了i,则玩家1手里有num[i]分,则玩家2一定会按照[i+1…j]这个子数组中的最优拿法去拿,于是玩家2此时手里相当于有dp[i+1][j]分,于是玩家1比玩家2多num[i]-dp[i+1][j]分。如果玩家1先手拿了j,则玩家1手里有num[j]分,则玩家2一定会按照[i…j-1]这个子数组中的最优拿法去拿,于是玩家2此时手里相当于有dp[i][j-1]分,于是玩家1比玩家2多num[j]-dp[i][j-1]分。数组的填充方向是从下往上,从左到右,最后填充的是dp[1][n]。