常用算法-动态规划dp

本文介绍了动态规划方法在解决一系列经典问题中的应用,如最大子序列和、股票买卖问题等。通过确定状态、转移方程和边界条件,构建了从一维到多维的动态规划模型,有效地求解了不同限制下的最优策略。例如,求解最多能获取多少利润的股票交易问题,从不允许卖出到允许多次交易,甚至限制交易次数,动态规划都能提供解决方案。
摘要由CSDN通过智能技术生成

利用历史数据记录推导新问题的解
经典问题:青蛙爬楼梯、斐波那契数列、01背包问题、最大序列和、路径问题等
解题思想:确定状态(最后一步:选择or不选择,子问题划分**),转移方程(dp[i][j] 与 dp[i-1][j-1]…的关系),开始和边界条件

题型一
最大子序列和 - 力扣53.
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
在这里插入图片描述

int maxSubArray(vector<int>& nums) {
        int n=nums.size();
        int maxsum=nums[0];
        if(n==1)
            return nums[0];
        vector<int> dp(n);
        dp[0]=nums[0];
        //考虑边界条件,数组只有1个
        for(int i=1;i<n;i++){
            dp[i]=max(nums[i],dp[i-1]+nums[i]);
            maxsum=max(dp[i],maxsum);
        }
        return maxsum;
    }

股票买卖问题 - 力扣121
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
在这里插入图片描述

int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n==0) 
            return 0;
        int minprice=prices[0];
        vector<int> dp;
        dp.resize(n);
        dp[0]=0;
        for(int i=1;i<n;i++){
            dp[i]=max(dp[i-1],prices[i]-minprice);
            minprice=min(prices[i],minprice);
        }
        return dp[prices.size()-1];
    }

二维dp,确定状态(选择or不选择)
多次买卖股票最多存有一只股票在手 - 力扣122
给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
在这里插入图片描述

	int maxProfit(vector<int>& prices) {
        int n=prices.size();
        //dp算法,第i天可能两种情况,没有股票在手dp[i][0],有一只股票在手dp[i][1]
        //dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
        //dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
        vector<vector<int>> dp(n,vector<int>(2));
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<n;i++){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
        }
        return dp[n-1][0];
    }

多维dp - 力扣123
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
三维数组

 int maxProfit(vector<int>& prices) {
        if(prices.size()==0) return 0;
        //dp[i][j] 第i天 的 j 操作
        //j=0 没有操作
        //j=1 第一次买入
        //j=2 第一次卖出
        //j=3 第二次买入
        //j=4 第二次卖出
        //当天操作的最大值可能是 i-1 的记录,或者 i 天操作
        
        vector<vector<int>> dp(prices.size(),vector<int>(5,0));
        dp[0][1]=-prices[0];
        dp[0][3]=-prices[0];
        
        for(int i=1;i<prices.size();i++){
            dp[i][0]=dp[i-1][0];
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
            dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]);
            dp[i][3]=max(dp[i-1][3],dp[i-1][2]-prices[i]);
            dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[prices.size()-1][4];
    }
int maxProfit(vector<int>& prices) {
        //dp[天数][是否持股][卖出次数]
        if(prices.size()<=1) return 0;
        int dp[prices.size()][2][3];
        
        dp[0][0][0]=0;//第一天没有买入卖出
        dp[0][0][1]=dp[0][1][1]=INT_MIN/2;//防止后面加减越界,选择一个极小值的的一半,不影响结果
        dp[0][0][2]=dp[0][1][2]=INT_MIN/2;//防止后面加减越界,选择一个极小值的的一半,不影响结果
        dp[0][1][0]=-prices[0];
        
        for(int i=1;i<prices.size();i++){
            dp[i][0][0]=0;
            dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1]);
            dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2]);
            dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0]);
            dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1]);
            dp[i][1][2]=INT_MIN;
        }
        return max(0,max(dp[prices.size()-1][0][1],dp[prices.size()-1][0][2]));
    }

最多交易k次

int maxProfit(int k, vector<int>& prices) {
        //n天最多交易n/2次,所以k最大为n/2
        //dp0 一直没买
        //dp1 买了1次
        //dp2 买1卖1
        //dp3 买2卖1
        //dp4 买2卖2
        //...
        //dp2k 买k卖k
        int n=prices.size();
        if(n<=1||k==0) return 0;
        k=min(n/2,k);
        vector<int> dp(2*k+1,INT_MIN/2);
        dp[0]=0;
        dp[1]=-prices[0];
        
        for(int i=1;i<n;i++){
            for(int j=1;j<2*k+1;j++){
                if(j&1==1){
                    dp[j]=max(dp[j],dp[j-1]-prices[i]);
                }
                else{
                    dp[j]=max(dp[j],dp[j-1]+prices[i]);
                }
            }
        }
        return dp[2*k];
        
    }

题型二
不同路径总数 - 力扣62
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?
在这里插入图片描述

int uniquePaths(int m, int n) {
        vector<vector<int>> f(m, vector<int>(n));
        for (int i = 0; i < m; ++i) {
            f[i][0] = 1;
        }
        for (int j = 0; j < n; ++j) {
            f[0][j] = 1;
        }
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                f[i][j] = f[i-1][j] + f[i][j-1];
            }
        }
        return f[m-1][n-1];
    }

最小路径和 - 力扣64
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。每次只能向下或者向右移动一步。
在这里插入图片描述

 int minPathSum(vector<vector<int>>& grid) {
        if (grid.size()==0 || grid[0].size() == 0) {
            return 0;
        }
        int rows = grid.size(), columns = grid[0].size();
        auto dp = vector<vector<int>> (rows, vector<int>(columns));
        dp[0][0] = grid[0][0];
        for (int i = 1; i < rows; i++) {
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for (int j = 1; j < columns; j++) {
            dp[0][j] = dp[0][j-1] + grid[0][j];
        }
        for (int i = 1; i < rows; i++) {
            for (int j = 1; j < columns; j++) {
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[rows-1][columns-1];
    }

解码方法 - 力扣91
在这里插入图片描述

int numDecodings(string s) {
        int n=s.size();
        vector<int> dp(n+1);
        dp[0]=1;
        for(int i=1;i<=n;i++){
            //当前字符不是0,即可以解码为一个字母,解码方案与前一个相同dp[i]=dp[i-1]
            if(s[i-1]!='0')
                dp[i]=dp[i]+dp[i-1];
            //当前字符与前一个字符(非0)可以组成小于等于26的数字,解码方案可以增加dp[i]=dp[i-1]+dp[i-2]
            if(i>1&&s[i-2]!='0'&&((s[i-2]-'0')*10+(s[i-1]-'0')<=26))
                dp[i]=dp[i]+dp[i-2];
        }
        return dp[n];
    }

题型三
编辑距离 - 力扣72
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。你可以对一个单词进行如下三种操作:
1插入一个字符 dp[i][j] = dp[i][j-1]+1
2删除一个字符 dp[i][j] = dp[i-1][j]+1
3替换一个字符 dp[i][j] = dp[i-1][j-1]+1

int minDistance(string word1, string word2) {
        int m=word1.size(),n=word2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        dp[0][0]=0;
        for(int i=1;i<=n;i++){
            dp[0][i]=dp[0][i-1]+1;
        }
        for(int i=1;i<=m;i++){
            dp[i][0]=dp[i-1][0]+1;
        }
        
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1];
                else dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
            }
        }
        return dp[m][n];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值