leetcode常见动态规划问题

leetcode中常见动态规划题目

买卖股票的最佳时机

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股股票。你也可以先购买,然后在 同一天出售。返回你能获得的最大利润 。

题解思路:

在第i天有两种状态,持有股票或者不持有股票,所以定义状态 d p [ i ] [ 0 ] dp[i] [0] dp[i][0]:第i天无股票所能够获得的最大利润, d p [ i ] [ 1 ] dp[i][1] dp[i][1]:第i天有股票。该问题就在于求解 m a x ( d p [ n ] [ 0 ] ) max(dp[n][0]) max(dp[n][0])

第i天无股票所获得的最大利润只有两种情况,第i-1天无股票或者在第i-1天有股票而在第i天将股票卖出,状态转移方程如下:
d p [ i ] [ 0 ] = m a x { d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] } dp[i][0] = max\{dp[i-1][0],dp[i-1][1]+prices[i] \} dp[i][0]=max{dp[i1][0],dp[i1][1]+prices[i]}
第i天有股票所获得的最大利润只有两种情况,第i-1天有股票或者在第i天买入,状态转移方程如下:
d p [ i ] [ 1 ] = m a x { d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] } dp[i][1] = max\{dp[i-1][1],dp[i-1][0]-prices[i]\} dp[i][1]=max{dp[i1][1],dp[i1][0]prices[i]}
定义出口: d p [ 0 ] [ 0 ] = 0 , d p [ 0 ] [ 1 ] = − p r i c e s [ 0 ] . dp[0][0] =0,dp[0][1]=-prices[0]. dp[0][0]=0,dp[0][1]=prices[0].

AC代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int dp[prices.size()][2];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for (int i=1;i<prices.size();i++){
            dp[i][0]=max(dp[i-1][1]+prices[i],dp[i-1][0]);
            dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]); 
        }
        return dp[prices.size()-1][0];
    }
};
跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

opt[i]表示到达下标i所需要的最短步长,opt[i]的值就等于在所有能到达下标i的位置k中,选择一个最小的opt[k]+1得到opt[i]。状态转移方程:
o p t [ i ] = m i n { o p t [ i − j ] + 1 } 对 所 有 满 足 n u m s [ i − j ] > = j 的 j opt[i] = min \{ opt[i-j]+1\} \quad 对所有满足nums[i-j]>=j的j opt[i]=min{opt[ij]+1}nums[ij]>=jj
出口opt[0] = 0

AC代码:

class Solution {
public:
    int jump(vector<int>& nums) {
        int dp[nums.size()+1];
        for (int i=0;i<nums.size()+1;i++){
            dp[i]=100000;
        }
        dp[0]=0;
        for (int i=1;i<nums.size();i++){
            for (int j=1;j<=i;j++){
                if (nums[i-j]>=j){
                    if (dp[i-j]+1<dp[i]){
                        dp[i]=dp[i-j]+1;
                    }
                }
            }
        }
        return dp[nums.size()-1];
    }
};
不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

d p [ i ] [ j ] dp[i][j] dp[i][j]表示从起点到坐标(i-1,j-1)的路径总数。机器人每次智能向下或者向右移动。

状态转移方程:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] f o r i > 0 & & j > 0 d p [ i ] [ 0 ] = 1 f o r i ≥ 0 d p [ 0 ] [ j ] = 1 f o r j ≥ 0 dp[i][j] = dp[i-1][j] + dp[i][j-1] \quad for \quad i>0 \&\& j>0 \\ dp[i][0] = 1 \quad for \quad i \geq0 \\ dp[0][j] = 1 \quad for \quad j \geq0 \\ dp[i][j]=dp[i1][j]+dp[i][j1]fori>0&&j>0dp[i][0]=1fori0dp[0][j]=1forj0
AC代码:

class Solution {
public:
    int uniquePaths(int m, int n) {
        int dp[m][n];
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for (int j=1;j<n;j++){
            dp[0][j]=dp[0][j-1];
        }
        for (int i=1;i<m;i++){
            dp[i][0]=dp[i-1][0];
        }
        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];
    }
};

不同路径2

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

状态转移方程与上面并无差别,但是需要注意:一旦遇到障碍点,需要将 d p [ i ] [ j ] dp[i][j] dp[i][j]置0

AC代码:

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int dp[obstacleGrid.size()][obstacleGrid[0].size()];
        memset(dp,0,sizeof(dp));
        if (obstacleGrid[0][0]==1){
            return 0;
        }
        else{
            dp[0][0]=1;
        }
        int flag1=1;
        for (int j=1;j<obstacleGrid[0].size();j++){
            if (obstacleGrid[0][j]==1){
                flag1=0;
            }
            if (flag1==1){
                dp[0][j]=1;
            }
            else{
                dp[0][j]=0;
            }
        }
        int flag2=1;
        for (int i=1;i<obstacleGrid.size();i++){
            if (obstacleGrid[i][0]==1){
                flag2=0;
            }
            if (flag2==1){
                dp[i][0]=1;
            }
            else{
                dp[i][0]=0;
            }
        }
        for (int i=1;i<obstacleGrid.size();i++){
            for (int j=1;j<obstacleGrid[0].size();j++){
                if (obstacleGrid[i][j]==1){
                    dp[i][j]=0;
                }
                else{
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
        }
        int res=dp[obstacleGrid.size()-1][obstacleGrid[0].size()-1];
        return res;
    }
};

最小路径和

给定一个包含非负整数的 *m* x *n* 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

**说明:**每次只能向下或者向右移动一步。

d p [ i ] [ j ] dp[i][j] dp[i][j]表示到达坐标(i-1,j-1)的最小数字总和。

状态转移方程:
d p [ i ] [ j ] = m i n { d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] } + g r i d [ i ] [ j ] f o r i > 0 & & j > 0 d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] + g r i d [ i ] [ 0 ] f o r i > 0 d p [ 0 ] [ j ] = d p [ 0 ] [ j − 1 ] + g r i d [ 0 ] [ j ] f o r j > 0 dp[i][j] = min\{ dp[i-1][j] ,dp[i][j-1]\} + grid[i][j] \quad for \quad i>0 \&\& j>0 \\ dp[i][0] = dp[i-1][0] + grid[i][0] \quad for \quad i > 0 \\ dp[0][j] = dp[0][j-1] + grid[0][j] \quad for \quad j > 0 \\ dp[i][j]=min{dp[i1][j],dp[i][j1]}+grid[i][j]fori>0&&j>0dp[i][0]=dp[i1][0]+grid[i][0]fori>0dp[0][j]=dp[0][j1]+grid[0][j]forj>0
d p [ 0 ] [ 0 ] = g r i d [ 0 ] [ 0 ] dp[0][0] = grid[0][0] dp[0][0]=grid[0][0]

AC代码:

class Solution {
public:
    int min(int a,int b){
        if (a<b){
            return a;
        }
        else{
            return b;
        }
    }
    int minPathSum(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        int dp[m][n];
        memset(dp,0,sizeof(dp));
        dp[0][0]=grid[0][0];
        for (int i=0;i<m;i++){
            for (int j=0;j<n;j++){
                if (i==0&&j==0)
                    continue;
                if (i==0)
                    dp[i][j]=dp[i][j-1]+grid[i][j];
                else if (j==0)
                    dp[i][j]=dp[i-1][j]+grid[i][j];
                else{
                    dp[i][j]=min(dp[i][j-1],dp[i-1][j])+grid[i][j];
                }
            }
        }
        int res=dp[m-1][n-1];
        return res;
    }
};

买卖股票最佳时期含冷冻期

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

新增状态: d p [ i ] [ 2 ] dp[i][2] dp[i][2]:表示第i天处于冷冻期

对于 d p [ i ] [ 0 ] dp[i][0] dp[i][0]:第i-1天处于冷冻期或者第i-1天无股票且不在冷冻期
d p [ i ] [ 0 ] = m a x { d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 2 ] } dp[i][0] = max\{ dp[i-1][0],dp[i-1][2]\} dp[i][0]=max{dp[i1][0],dp[i1][2]}
对于 d p [ i ] [ 1 ] dp[i][1] dp[i][1]:第i-1天有股票或者第i天买入
d p [ i ] [ 1 ] = m a x { d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] } dp[i][1] = max\{ dp[i-1][1],dp[i-1][0]-prices[i]\} dp[i][1]=max{dp[i1][1],dp[i1][0]prices[i]}
对于 d p [ i ] [ 2 ] dp[i][2] dp[i][2]:第i天卖出股票
d p [ i ] [ 2 ] = d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] dp[i][2] = dp[i-1][1]+prices[i] dp[i][2]=dp[i1][1]+prices[i]
结果: max{ d p [ n ] [ 0 ] , d p [ n ] [ 2 ] dp[n][0],dp[n][2] dp[n][0],dp[n][2]}

AC代码:

class Solution {
public:
    int Max(int a,int b,int c){
        int k=max(a,b);
        int j=max(b,c);
        return max(k,j);
    }
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        int dp[len][3];
        memset(dp,0,sizeof(dp));
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        dp[0][2]=0;
        for (int i=1;i<len;i++){
            dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
            dp[i][2]=dp[i-1][1]+prices[i];
            dp[i][0]=max(dp[i-1][0],dp[i-1][2]);
        }
        return max(dp[len-1][0],dp[len-1][2]);
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值