动态规划

【动态规划概述】

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程最优化的数学方法。
它是20世纪50年代初美国数学家R.E.Bellman等人提出的最优化原理,它利用各阶段之间的关系,
逐个求解,最终求得全局最优解。

1.【确认原问题与子问题】

2.【动态规划状态】

3.【边界状态的值】

4.【状态转移方程】

一,爬楼梯

LeetCode 70. Climbing Stairs

1.数组的初始化,大小是函数的参数?

用变量定义数组长度时,不可以初始化
但是vector可以vector< dp >( size,value)

2.子数组连续,可以递归

3. 到达第i阶的方式数量 = 到达第i-1阶的方式数量 + 到达第i-2阶的方式数量

4.状态单一

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int climbStairs(int n) {
       // int dp[n]={0};//dp[n]不行
        vector<int>dp(n+3,0);//dp(n,0)会溢出
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/climbing-stairs/solution/pa-lou-ti-by-xia-mu-lao-zhang-ren-pw01/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二,打家劫舍

LeetCode 198. House Robber

1.连续子数组,可以递归

2.状态方程的提取

3.状态不单一

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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-1],dp[i-2]+nums[i]);
        }
        return dp[nums.size()-1];
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-xia-mu-lao-zhang-ren-clpi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

三,最大子序和

LeetCode 53. Maximum Subarray

方法一,动态规划

1.dp[i]代表【以第i个元素结尾】的最长递增子序列,之前是从头开始

2.更新dp[i]条件(状态转移方程)

3.子数组不连续,不能直接递归,和前两个不一样

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==0){
            return 0;
        }
        vector<int>dp(nums.size(),0);//dp[i]代表以第i个元素结尾的最长递增子序列
        dp[0]=1;//边界
        int LIS=1;
        for(int i=1;i<dp.size();i++){
            dp[i]=1;//单个元素最小长度是1
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]&&dp[i]<dp[j]+1){//后面的数比前面的数大,且当前状态长度比后面状态长度+1小,更新dp【i】
                    dp[i]=dp[j]+1;
                }
            }
            if(LIS<dp[i]){
                LIS=dp[i];
            }
        }
        return LIS;       
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-by-xia-mu-la-ukwd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法二,栈

1.用vector实现栈

2.优化查找

四,找零钱

LeetCode 322. Coin Change

1.状态转移【枚举所有和dp[i]有关的状态】【状态不单一】

2.更新最优解【状态转移方程】

3.前一状态是否可以达到

4.vector的初始化【为什么会溢出】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        //vector<int>dp(amount,-1);//为啥会溢出啊?????????
        vector<int>dp;
        for(int i=0;i<=amount;i++){
            dp.push_back(-1);
        }
        dp[0]=0;//边界
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.size();j++){//dp[i]代表达到金额i所需要的最小张数
                if(i-coins[j]>=0&&dp[i-coins[j]]!=-1){//当前金额包含面值,或上一状态已达到
                    if(dp[i]==-1||dp[i]>dp[i-coins[j]]+1){//若当前状态未达到,或所需张数更小,则更新
                        dp[i]=dp[i-coins[j]]+1;
                    }
                }
            }
        }
        return dp[amount];
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/coin-change/solution/ling-qian-dui-huan-by-xia-mu-lao-zhang-r-v9ty/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

五,三角形

LeetCode 120. Triangle

1.二维dp

2.自上而下

3.涉及考虑边界

3.从下往上,更少考虑边界,【反向思维】

4.无效化【不理解】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        if(triangle.empty()||triangle[0].empty()){
            return 0;
        }
        int m=triangle.size();
        int dp[m][m];//初始化状态转移矩阵,表示从(i,j)到底部的最小路径和
        for(int i=0;i<m;i++){
            dp[m-1][i]=triangle[m-1][i];//最低行个节点的最小路径为节点本身
        }
        for(int i=m-2;i>=0;i--){//填充状态转移矩阵
            for(int j=0;j<i+1;j++){
                dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j];
            }
        }
        return dp[0][0];//即为题解
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/triangle/solution/san-jiao-xing-zui-xiao-lu-jing-he-by-xia-gmn7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int m=triangle.size();
        vector< vector<int> >dp(m,vector<int>(m,INT_MAX));
        dp[0][0]=triangle[0][0];
        for(int i=1;i<m;i++){
            dp[i][0]=dp[i-1][0]+triangle[i][0];//第一列只能从正上方下来
            for(int j=1;j<i+1;j++){//考虑每一列
                dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+triangle[i][j];//无效化
            }
        }
        int res= INT_MAX;
        for(int j=0;j<m;j++){
            res=min(res,dp[m-1][j]);//找到最后一行的最小值为题解
        }
        return res;
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/triangle/solution/san-jiao-xing-zui-xiao-lu-jing-he-by-xia-e8nh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

六,最长上升子序列

LeetCode 300. Longest Increasing Subsequence

方法一,

1.两个状态i,思考和最大子段和的相似之处

2.下标和个数差1【老问题了】

3.动态规划边界

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==0){
            return 0;
        }
        vector<int>dp(nums.size(),0);//dp[i]代表以第i个元素结尾的最长递增子序列
        dp[0]=1;//边界
        int LIS=1;
        for(int i=1;i<dp.size();i++){
            dp[i]=1;//单个元素最小长度是1
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]&&dp[i]<dp[j]+1){//后面的数比前面的数大,且当前状态长度比后面状态长度+1小,更新dp【i】
                    dp[i]=dp[j]+1;
                }
            }
            if(LIS<dp[i]){
                LIS=dp[i];
            }
        }
        return LIS;       
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-by-xia-mu-la-ukwd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法二,

1.用vector实现的栈

2.二分查找优化,【index=-1】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int binary_search(vector<int> nums,int target){
    int index=-1;
    int begin=0;
    int end=nums.size()-1;
    while(index==-1){
        int mid=(begin+end)/2;
        if(target==nums[mid]){//如果target在栈中,直接取mid
            index= mid;
        }
        else if(target>nums[mid]){//如果不在栈中,则找到应该插入的位置并【替换他】
            if(mid==nums.size()-1||target<nums[mid+1]){
                index=mid+1;//没有加1
            }
            begin=mid+1;
        }
        else if(target<nums[mid]){
            if(mid==0||target>nums[mid-1]){
                index =mid;
            }
            end=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 pos=binary_search(stack,nums[i]);
                stack[pos]=nums[i];
            }
        }
        return stack.size();//还可以返回具体的序列信息
        
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-by-xia-mu-la-gcvg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

七,最小路径和

LeetCode 64. Minimum Path Sum

1.【处理边界】,才能好递归

就看看运动的方向,往后看有跑到界外的吗

2.与【五,三角形的相似之处】

3.原始数组,最小值数组

4.处理边界

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if(grid.size()==0){
            return 0;
        }
        int row=grid.size();
        int column=grid[0].size();
        vector<vector<int> >dp(row,vector<int>(column,0));
        dp[0][0]=grid[0][0];//边界
        for(int i=1;i<column;i++){
            dp[0][i]=dp[0][i-1]+grid[0][i];//第一行边界
        }
        for(int i=1;i<row;i++){
            dp[i][0]=dp[i-1][0]+grid[i][0];//第一列边界
            for(int j=1;j<column;j++){
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[row-1][column-1];
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/minimum-path-sum/solution/zui-xiao-lu-jing-he-by-xia-mu-lao-zhang-at6fb/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

八,地牢游戏

LeetCode 174. Dungeon Game

1.两个方向思考

2.由左上到右下:因为有两个重要程度相同的参数同时影响后续的决策

3.反向递归,用二维数组代表每个格子的状态,这个状态是什么

4.注意边界的无效值,赋为极大值使之无效化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int n=dungeon.size();
        int m=dungeon[0].size();
        vector< vector<int> >dp(n+1,vector<int>(m+1,  INT_MAX));//边界无效化
        dp[n][m-1]=dp[n-1][m]=1;//特别地,无效化【处理一下边界,看看起点,至少剩一滴血】
        for(int i=n-1;i>=0;i--){
            for(int j=m-1;j>=0;j--){
                int minn=min(dp[i+1][j],dp[i][j+1]);
                dp[i][j]=max(minn-dungeon[i][j],1);//至少为1
            }
        }
        return dp[0][0];

    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/dungeon-game/solution/di-xia-cheng-you-xi-by-xia-mu-lao-zhang-xgcl9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值