动态规划特辑-01-基础篇
动态规划特辑-01
一、动态规划的题目特点
1、计数
- 有多少种方法走到右下角
- 有多少种方法选出k个数使得和为sum
2、求最大值最小值
- 从左上角走到右下角路径的最大数字和
- 最长上升子序列长度
3、求存在性
- 取石子游戏,先手是否必胜
- 能不能取出k个数使得和是sum
二、求最值型动态规划——案例讲解解题步骤
1、问题描述---求最大最小值的动态规划

2、动态规划组成部分一:确定状态
- 简单的说,解决动态规划问题的时候需要开一个数组,不管是几维的数组,我们都要明白数组的每个元素f[i]或者f[i][j]都代表什么。
- 确定状态需要两个意识:
①最后一步
目的是通过最后一步,把问题转化成子问题。


②子问题


③递归解法

递归解法的问题

f(20重复了3次,递归重复了好多次,等到程序的数量特别庞大的时候,就会造成问题。
3、动态规划组成部分二:转移方程

4、动态规划组成部分三:初始条件和边界情况
初始条件就是把最小的情况定义下来,边界情况就是不要数组越界。

5、动态规划组成部分四:计算顺序
从大到小还是从小到大去计算

顺序演示:
正无穷代表拼不出来

最后,可以算出f[27]要用5枚硬币。
计算次数
由于没有重复的计算,相比于递归,动态规划的速度很快。

6、小结

7、题目练习——lintcode-669.换硬币
①问题描述


②代码解决
需要注意的点:
- 数组开辟的大小
- 初始化f[0]
- f[i]的处理,Integer的最大值
- 转移方程
public class Solution {
    /**
     * @param coins: a list of integer
     * @param amount: a total amount of money amount
     * @return: the fewest number of coins that you need to make up
     */
    public int coinChange(int[] coins, int amount) {
        //开多大的数组呢?amount就是总量
        //如果要用到n,我们就要开辟数组[n+1],因为下标从0开始
        //如果要用到n-1,我们就开辟数组[n]
        int[] f = new int[amount + 1];
        int n = coins.length;//number of kinds of coins
        //initialization
        f[0] = 0;
        int i,j;
        //f[1] f[2] ... f[27]
        for (i = 1; i <= amount; ++i){
            f[i] = Integer.MAX_VALUE;
            //last coinA[j]
            //f[i] = min{f[i-A[0]]+1,...,f[i-A[n-1]]+1]}
            for (j = 0; j < n; ++j){
                //编程细节的判断,很重要!越界的判断
                if (i >= coins[j] && f[i -coins[j]] != Integer.MAX_VALUE){
                    f[i] = Math.min(f[i - coins[j]]+1, f[i]);
                }
                
            }
        }
        if (f[amount] == Integer.MAX_VALUE){
            f[amount] = -1;
        }
        return f[amount];
    }
}
三、计数型动态规划——机器人路径问题
1、题目描述

2、步骤一:确定状态
①最后一步

②子问题

3、步骤二:转移方程

4、步骤三:初始条件和边界情况

5、步骤四:计算顺序
如何确定计算顺序呢?根据转移方程来,在算右下角的格子时候,左面的格子刚刚算得到,上面的格子已经得到。

6、题目练习——lintcode-114.不同的路径
①问题描述

②代码解决
- 这里把初始化放到了for循环里面,为什么不在外面写呢?因为for循环会把所有格子都遍历一遍,所以直接写到里面就好了。
public class Solution {
    /**
     * @param m: positive integer (1 <= m <= 100)
     * @param n: positive integer (1 <= n <= 100)
     * @return: An integer
     */
    public int uniquePaths(int m, int n) {
        //注意此处为什么之开辟m到n,因为最终的结果只用到[m-1,n-1]
        int[][] f = new int[m][n];
        int i,j;
        for (i=0;i<m;i++){//row: top to bottom
            for(j=0;j<n;j++){//column: left to right
                if(i==0 || j==0){
                    f[i][j] = 1;
                }else{
                    f[i][j] = f[i-1][j] + f[i][j-1];
                }
            }
        
        }
    return f[m-1][n-1];
    }
}
四、存在型可行性型动态规划——Jump Game
1、题目描述

2、步骤一:确定状态
①最后一步

通过最后一步来确定子问题
②子问题

3、步骤二:转移方程

4、步骤三:初始条件和边界情况

5、步骤四:计算顺序

6、题目练习——lintcode-116.跳跃游戏
①问题描述


②代码解决
public class Solution {
    /**
     * @param A: A list of integers
     * @return: A boolean
     */
    public boolean canJump(int[] A) {
        int n = A.length;
        boolean[] f = new boolean[n];
        f[0] = true;//initialization
        for (int j=1;j < n;j++){
            f[j] = false; //假设不能通过
            //previous stone i
            //last jump is from i to j
            for (int i=0; i < j; i++){
                if(f[i] && i + A[i] >= j){
                    f[j] = true;
                    break;
                }
            }
        }
        return f[n-1];
    }
}
五、后续学习方向


 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号