九、动态规划(小象)

动态规划(dynamic programming  : dp)的解题技巧:

动态规划的出发点:枚举不是很理想,枚举没有利用题目的一些性质

代码量不会很大,思考出来就可以写;通过做题 来熟悉动态规划

1、确定动态规划数组dp[]中dp[i]代表什么状态?

2、确定每两个相邻状态之间有什么联系?dp[i] 和 dp[i-1]?

3、dp数组当中的dp[0]要会使用,可以减少边界条件的判定。

70、爬楼梯

思考:用递归做会超时,使用动态规划的方法来做。

class Solution {
public:
    int climbStairs(int n) {
      vector<int> dp(n+3,0); //n + 3 输入n = 0 n= 1 n = 2 都不用再做特殊处理
        dp[1] = 1 ;
        dp[2] = 2;
        for(int i = 3 ; i<= n ; i++){
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
        
    }
};

198、打家劫舍

扫描二维码关注公众号,回复: 5095903 查看本文章

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 0 ){
            return 0;
        }
        if(nums.size() == 1){
            return nums[0];
        }
        vector<int> dp(nums.size() ,0);
        dp[0] = nums[0];
        dp[1] = max(nums[0],nums[1]);
        for(int i = 2 ;i < nums.size();i++){
            dp[i] = max(dp[i-2] + nums[i],dp[i-1] );
        }
        return dp[nums.size() - 1 ];
    }
};

53、最大子序和

思考:

1、假设i状态代表前i个数字组成的连续最大字段和?可以嘛?不行

2、假设状态i表示以nums[i]结尾的子段和?

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
       if(nums.size() == 1 ){
           return nums[0];
       } 
        vector<int> dp(nums.size(),0);//dp[0] 要有对应的值
        dp[0] = nums[0];
        int max_res = dp[0];
        for(int i = 1 ; i < nums.size(); i++){
            dp[i] = max(dp[i -1] + nums[i] ,nums[i]);
            if(max_res < dp[i]){
                max_res = dp[i];
            }
        }
        return max_res;
    }
};

322、零钱兑换

思考: 选择贪心?还是dp?在固定面值的情况下,贪心是可以的!但是题目中并没有固定!

状态i : dp[i]表示当前金额下的最小组合张数。

设置边界条件,dp[0] = 0;使得属于面值的金额可以一起运算,不用单独初始化。即当前的面值的张数,等于减去对应的纸币面额后的张数 +1。

题目类似于最基础的1,2题。可以把金额想象成总的台阶数,而钱币面值就是每次攀爬的层数。

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+ 1 ,-1);
        dp[0] = 0;
        for(int i = 1 ; i <= amount; i ++){
            for(auto it : coins){
                if( i - it >= 0 && dp[i - it] != -1){
                    if(dp[i] == -1 || dp[i] > dp[i-it] + 1 ){
                        dp[i] = dp[i - it] + 1 ;
                    }
                }
            }
        }
        return dp[amount];
        }
};

120、三角形最短路径和

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        if(triangle.size() == 0){
            return 0;
        }
        vector<vector<int>> dp;
        for(int i = 0 ;i < triangle.size();i++){
            dp.push_back(vector<int>());
            for(int j = 0 ; j < triangle[i].size();j++){
                dp[i].push_back(0);
            }
        }
        for(int i = 0 ;i <triangle[triangle.size()-1].size();i++){
            dp[triangle.size()-1][i] = triangle[triangle.size()-1][i];
        }
        for(int i = dp.size() - 2 ;i >= 0; i--){
            for(int j = 0 ; j < dp[i].size();j++){
                dp[i][j] = min(dp[i+1][j],dp[i+1][j+1]) + triangle[i][j]; 
            }
        }
        return dp[0][0];
    }
};

300、最长上升子序列

思考:两种状态

1、以i个数字组成的数组中找一个最长上升子序列

2、以第i个数字为最长上升子序列最后一个数字(以i个数字结尾!判断是双重循环,要判断i-1个数字的大小,并且当dp[i] < dp[j] +1 ,要更新)

Longest Increasing Subsequence,LIS,最长上升子序列

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size() == 0){
            return 0;
        }
       vector<int> dp(nums.size(),1);
        int LIS = 1;
    for(int i = 1;i<nums.size();i++){
        for(int j =0;j <= i -1 ;j++ ){
            if(nums[i] > nums[j] && dp[i] < dp[j] + 1 ){
                dp[i] = dp[j] + 1;
            }
        }
        if(LIS < dp[i]){
            LIS = dp[i];
        }
    }
        return LIS;
    }
};

方法2:使用栈

代码:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size() == 0){
            return 0;
        }
      vector<int> _stack;
        _stack.push_back(nums[0]);
      for(int i = 1 ;i < nums.size(); i++){
         if(nums[i] > _stack.back()){
             _stack.push_back(nums[i]);
         }
          else{
               for(int j = 0; j <_stack.size() ; j++){
                    if(nums[i] <= _stack[j] )/*key step !*/{
                        _stack[j] = nums[i];
                        break;
                    }
               }
          }
      }  
        return _stack.size();
    }
};

当中为什么是num[i] <= _stack[j],如下如果栈中是{4,10},此时传入4,如果是<的话。那么覆盖的就是10。然后的结果就会有问题。

题目当中说用O(n²)优化到O(nlogn),这里可以因为存入栈中是有序的,所以可以使用二分查找,来替代一次次的从头遍历。

二分查找几个常犯的错误:

1、中间部分是mid = (begin + end )/ 2;

2、mid每次循环是要改变的,所以再循环体之内,每次要重新定义初始化。

3、二分查找,首先检查mid,不在就去搜索左区间,[begin,mid -1 ] ;否则就去搜索右区间,[mid + 1 ,end ],然后来更新begin 和end

4、题目中涉及到插入位置,在左右区间的时候会有。左区间:如果mid == 0 || target > vec[mid -1 ] ,此时就应该index = mid ;右区间: 如果mid == end || target < vector[mid +1 ],此时index = mid +1。

int binary_search_insert(vector<int> &vec,int target){
    int index = -1;
    int begin = 0;
    int end = vec.size() - 1 ;
   // int mid = (end - begin)/2 ; 二分查找,mid在循环体之中,每次是变化
    //更重要的是!中间部分!是首项加尾项/2!!!!
    while(index == -1 ){
        int mid = (end + begin)/2;
        if(target == vec[mid]){
            index =  mid;
        }
        else if(target < vec[mid]){
            if(mid == 0 || target > vec[mid - 1]){
                index = mid;
            }
            end = mid -1;
        }
        else if( target > vec[mid]){
            if(mid == vec.size() -1 || target < vec[mid+1]){
                index = mid+1;/*一开始写成index = mid */
            }
            begin = mid +1;
        }
    }
    return index;
}
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size() == 0){
            return 0;
        }
      vector<int> _stack;
        _stack.push_back(nums[0]);
      for(int i = 1 ;i < nums.size(); i++){
         if(nums[i] > _stack.back()){
             _stack.push_back(nums[i]);
         }
          else{
            int index = binary_search_insert(_stack,nums[i]);
              _stack[index] = nums[i];
          }
      }  
        return _stack.size();
    }
};

64、最小路径和

思考:地图搜索的题目,比较简单。状态ij表示到达ij这个点的最短路径。收获还有明白了创建固定的二维vector和固定的二维vector的区别。

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if(grid.size() == 0){
            return 0;
        }
        int row = grid.size();
        int col = grid[0].size();
        vector<vector<int>> dp(row,vector<int>(col,0));
        //不知道尺寸的时候动态创建二维vector
      /* vector<vector<int>> dp;
        for(int i = 0 ; i< grid.size();i++){
            dp.push_back(vector<int>());
            for(int j = 0 ;j < grid[0].size();j++){
                dp[i].push_back(0);
            }
        }*/
         dp[0][0] = grid[0][0];
      /*  for(int i = 1; i < row;i++){
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }*/
         for(int j = 1; j < col;j++){
            dp[0][j] = dp[0][j-1] + grid[0][j];
        }
        for(int i = 1;i<row;i++){
             dp[i][0] = dp[i-1][0] + grid[i][0];
            for(int j = 1 ;j < col;j++){
                dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[row-1][col-1];
    }
};

174、地下城游戏

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        if(dungeon.size() == 0){
            return 0;
        }
        int row = dungeon.size();
        int col = dungeon[0].size();
        vector<vector<int>> dp(row,vector<int>(col,0));
        dp[row-1][col-1] = max(1,1-dungeon[row-1][col-1]);  
        for(int j = col -2 ; j >=0 ;j--){
            dp[row -1][j] = max(1,dp[row-1][j+1]-dungeon[row-1][j]);
        }      
        for(int i = row- 2 ;i>=0;i--){
             dp[i][col -1]=max(1,dp[i+1][col-1] - dungeon[i][col-1]);
            for(int j = col -2 ;j >= 0 ;j--){
                int dp_min = min(dp[i+1][j],dp[i][j+1]);
                dp[i][j] = max(1,dp_min - dungeon[i][j]);
            }
        }
        return dp[0][0];
    }
};

猜你喜欢

转载自blog.csdn.net/qq_34269988/article/details/85841205