动态规划小结
- 动态规划问题的一般形式就是求最值
- 判断算法问题是否具备「最优子结构」,即是否能够通过子问题的最值得到原问题的最值。
- 是否存在「重叠子问题」
- 「状态转移方程」
- 递归是自顶向下,递推是自底向上
- 做动态规划要先找到暴力解法,然后画出递归树,找到冗余的地方,再考虑用备忘录或者dp数组
509. 斐波那契数
暴力递归
class Solution {
public int fib(int n) {
if (n==0) return 0;
if (n==1) return 1;
return fib(n-1)+fib(n-2);
}
}
带备忘录的递归
class Solution {
public int fib(int n) {
int[] memo = new int[n+1];
return dp(memo, n);
}
int dp(int[] memo, int n){
if (n==0 || n==1){
return n;
}
if (memo[n]!=0) return memo[n];
memo[n] = dp(memo,n-1) + dp(memo,n-2);
return memo[n];
}
}
dp数组的递推解法
class Solution {
public int fib(int n) {
if (n<2) return n;
int[] memo = new int[n+1];
memo[0] = 0;
memo[1] = 1;
for (int i = 2;i<=n;i++){
memo[i] = memo[i-1]+memo[i-2];
}
return memo[n];
}
}
对递推空间的优化
class Solution {
public int fib(int n) {
if (n<2) return n;
int dp1 = 0;
int dp2 = 1;
for (int i=2;i<=n;i++){
int dp3 = dp1 + dp2;
dp1 = dp2;
dp2 = dp3;
}
return dp2;
}
}
322. 零钱兑换
- 找到规律简单,但是定义终止条件比较麻烦,以及对于非法解的处理,暴力解会超时,必须要用到备忘录或者dp数组
备忘录
class Solution {
int[] memo;
public int coinChange(int[] coins, int amount) {
memo = new int[amount+1];
Arrays.fill(memo, -666);
return dp(coins, amount);
}
int dp(int[] coins, int amount){
if (amount==0) return 0;
if (amount<0) return -1;
if (memo[amount]!=-666) return memo[amount];
int res = Integer.MAX_VALUE;
for (int i:coins){
int temp = dp(coins,amount-i);
if (temp==-1) continue;
res = Math.min(res, temp + 1);
}
memo[amount] = res== Integer.MAX_VALUE ? -1 : res;
return memo[amount];
}
}
dp数组迭代
class Solution {
public int coinChange(int[] coins, int amount) {
int[] memo = new int[amount+1];
Arrays.fill(memo, amount + 1);
for (int i=0;i<=amount;i++){
if (i==0) {
memo[0] = 0;
continue;
}
for (int j:coins){
if (i<j) continue;
memo[i] = Math.min(memo[i], memo[i-j]+1);
}
}
return memo[amount]==amount + 1? -1: memo[amount];
}
}
得物笔试
题目描述:小明用计算机随机生成了N个正整数,他希望从这N个数中选取若干个数,使得它们的和等于M,这些随机生成的数字可能会相同,但是每个数字最多只允许使用一次。当然这样的选取方案可能不存在,也可能有多个.现在希望你编写一个程序,能够找出数字个数最少的选取方案,输出对应的最少数字的个数,如果无解输出“Nosolution"