1.动态规划
1.1 递归
1.1.1 动态规划经典题目 - 斐波那契数列
题目:斐波那契数列的前两个为1,后面的为当前数字的前面两个只和(fib[i] = fib[i-1] + fib[i-2]),求斐波那契数列的第i个数字。
题解1 - 递归
public int recur_fib(int i){
if (i == 1 || i == 2){
return 1;
}
return recur_fib(i-1) + recur_fib(i-2);
}
问题:
小结:
- 因此我们可以得出,对于一些问题,我们需要通过某种方式来得到数据,并且我们将这些已经求得数据保存起来,当我们需要的时候直接使用,那么我们就可以减少不必要的运算执行。
- 并且我们可以看到斐波那契数列中除了开头的两个,后面的每一个数字的值只和它前面的两个数字有关。(fib(i) = fib(i-1) + fib(i-2) )
- 我们可以得出,需要求得的答案是在前面已有的基础上得到的。
- 递归其实可以看出来是一种第顶向下穷尽的方式,但是对于这道题,我们其实从底向上来做会更加方便(前一条观点)。
1.2 动态规划
- 创建一个数组来保存数据
- 已知条件,初始赋值
- 自底向上,求得结果
- 找寻动态规划转移方程
public int dp_fib(int i){
int[] dp = new int[i];
dp[0] = 1;
dp[1] = 1;
for (int j = 2; j < i ; j++) {
dp[j] = dp[j-1] + dp[j-2];
}
return dp[i-1];
}
1.3 题目练习
剑指 Offer 10- II. 青蛙跳台阶问题
分析:
public int numWays(int n) {
if (n == 0 || n == 1){
return 1;
}
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
LeetCode 746. 使用最小花费爬楼梯
分析:
public int minCostClimbingStairs(int[] cost) {
int len = cost.length;
int[] dp = new int[len];
dp[0] = cost[0];
dp[1] = cost[1];
for (int i = 2; i < len; i++) {
dp[i] = Math.min(dp[i-1],dp[i-2]) + cost[i];
}
return Math.min(dp[len-1],dp[len-2]);
}
LeetCode 62. 不同路径
分析:
- 创建一个数组来保存数据
- 已知条件初始赋值
- 自低向上,求得结果
- 设置动态转移方程
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for(int i=0; i<n; i++){
dp[0][i] = 1;
}
for(int i=0; i<m; i++){
dp[i][0] = 1;
}
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];
}
}
return dp[m-1][n-1];
}
LeetCode 343. 整数拆分
分析:
public int integerBreak(int n) {
if(n <= 2){
return 1;
}
int[] dp = new int[n+1];
dp[2] = 1;
for(int i=3; i<=n;i++){
for(int j=2;j<i; j++){
dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
}
}
return dp[n];
}
1.4 动态规划(背包问题)
1.4.1 经典题目:背包问题
分析:
- 每一个物品都有自己的价值和重量
- 背包的容量有限
- 每一个物品能装多次
public int findPackeMax(int[] value,int[] wight,int capacity){
int len = capacity;
int[] dp = new int[len+1];
dp[0] = 0;
for (int i = 1; i < wight.length; i++) {
for (int j = 0; j <= capacity; j++) {
if (j >= i)
dp[j] = Math.max(dp[j],value[i] + dp[j-wight[i]]);
}
}
return dp[len];
}
总结:
1.5 题目练习
LeetCode 518. 零钱兑换 II
分析:
public int change(int amount, int[] coins) {
int len = amount;
int[] dp = new int[len+1];
dp[0] = 1;
for(int i=0; i<coins.length; i++){
for(int j=1; j<=len; j++){
if(j >= coins[i] && dp[j - coins[i]] != 0){
dp[j] = dp[j] + dp[j-coins[i]];
}
}
}
return dp[len];
}
LeetCode 322. 零钱兑换
分析:
class Solution { public int coinChange(int[] coins, int amount) { int len = amount;
总结:
- 一定要动态的扩展物品和背包的容量
- 可以用二维数组
- 一些可以动态加入物品和动态扩展背包容量的题都可以用动态规划尝试解答
- 自底向上,获取有用数据,不断保存,需要时使用
- 和原有、没有加入新物品时的背包进行比较,得到最优解