动态规划题集(纯代码题)

最长回文串

给你一个字符串 s,找到 s 中最长的回文子串。
在这里插入图片描述

注:区别于公共子序列

class Solution {
public:
	string longestPalindrome(string s) {
	int b=0, l=0;//起点,终点指针
	bool dp[1000][1000] = { false };

	for (int i = s.length() - 1; i >= 0; i--) {//向前遍历起点指针,避免数据串联,
		for (int j = i; j < s.length(); j++) {//遍历终点指针
			if (j == i) dp[i][j] = true;
			else if (j == i + 1) dp[i][j] =s[i] == s[j];
			else {
				dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j];
			}
			if (dp[i][j]) {//看是不是最长的
				if (l - b < j - i) {
					b = i, l = j;
				}
			}
		}
	}
	return s.substr(b, l - b + 1);
    }
};

找路子

在这里插入图片描述

class Solution {
public:
    int getMaxValue(vector<vector<int>>& grid) {
        int n=grid.size(),m=grid[0].size();//为了方便
        vector<vector<int>> dp(n+1,(vector<int>(m+1)));//vector的构造函数       
        for(int i=1;i<=n;i++){//从1开始是为了避免下标小于0的问题
            for(int j=1;j<=m;j++){
                dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1];
                //grid数组下标还是从0开始的
            }
        }
        return dp[n][m];
    }
};

爬楼梯方法

在这里插入图片描述

class Solution {
public:
    int climbStairs(int n) {
        int dp[1010] = { 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];
    }
};

爬楼梯花费

在这里插入图片描述

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int top=cost.size();
        int dp[1010]={0};
        
        for(int i=2;i<=top;i++){
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
            //看走一步还是两步开销小
        }
        return dp[top];
    }
};

打家劫舍

在这里插入图片描述

class Solution {
public:
    int rob(vector<int>& nums) {
        if(!nums.size())
            return 0;
        if(nums.size()==1)
            return nums[0];
        vector<int> dp=vector<int>(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];
    }
};

打家劫舍Ⅱ

在这里插入图片描述

class Solution {
public:
    int rob(vector<int>& nums) {
        int N=nums.size();
        vector<int> dp=vector<int>(N,0);
        vector<int> dp2=vector<int>(N,0);
        if(nums.size()==1) return nums[0];
        if(nums.size()==2) return max(nums[0],nums[1]);
        dp[0]=nums[0];//1
        dp[1]=max(nums[0],nums[1]);//3
        for(int i=2;i<N-1;i++){ 
            dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
        }
        dp2[1]=nums[1];//3
        for(int i=2;i<N;i++){    
            dp2[i]=max(dp2[i-1],dp2[i-2]+nums[i]);
      
        }     
        return dp2[N-1]>dp[N-2]?dp2[N-1]:dp[N-2];
        //就看看要第一家和要最后一家谁更赚
    }
};

删除并获得点数

在这里插入图片描述

class Solution {
public:
    
    int deleteAndEarn(vector<int>& nums) {
        
        int N=nums.size();
        vector<int> trans=vector<int>(10010,0);
        for(int i=0;i<N;i++){
            trans[nums[i]]+=nums[i];//发现规律后转成set就变成打家劫舍了
        }
        vector<int> dp=vector<int>(10010);
       
        dp[0]=0;
        dp[1]=trans[1];
        for(int i=2;i<trans.size();i++){
            dp[i]=max(dp[i-2]+trans[i],dp[i-1]);
        }
        cout<<dp.size()<<endl;
        return dp[dp.size()-1];
    }
};

单词拆分

在这里插入图片描述

哈密顿

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

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=20,M=1<<20;

int dp[M][N];//一维代表路径集合,只关心用了哪个数字,不关心其顺序。二维代表路径终点。
int n;
int weight[N][N];

int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>weight[i][j];
        }
    }
    memset(dp,0x3f,sizeof dp);
    dp[1][0]=0;//0这个点进去集合了,相应的数位置为1,所以一维是1,000...001,状态压缩表示的路径
    for(int i=0;i<1<<n;i++){//遍历每一条可能的路径集合
        for(int j=0;j<n;j++){//遍历路径集合的按不同终点的排法
            if(i>>j&1){//这位有,代表要你做终点
                for(int k=0;k<n;k++){//计算状态转移,看从哪里走到终点最近
                    if((i-(1<<j))>>k&1){
                        dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+weight[k][j]);
                    }
                }
            }
        }
    }
    cout<<dp[(1<<n)-1][n-1];
    
}

跳跃游戏

其实也可看做走一步用一个能量,到了一个点上看捡地上的能量还是要自己手上的,二者取最大
在这里插入图片描述

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int N=nums.size();
        int farMost=0;//当前区间的右边界
        if(nums.size()==0)
            return true;
        for(int i=0;i<N;i++){
            if(i<=farMost){//从区间的点里找能到的最远的距离
                farMost=max(farMost,i+nums[i]);//更新最远距离
                if(farMost>=N-1)
                    return true;
            }
        }
        return false;
    }
};

跳跃游戏Ⅱ

在这里插入图片描述

class Solution {
public:
    int jump(vector<int>& nums) {
        int maxPos=0,n=nums.size(),end=0,step=0;
        //从区间里的点能到的最远距离,区间的边界
        for(int i=0;i<n-1;i++){
            if(maxPos>=i){//在区间内
                maxPos=max(maxPos,nums[i]+i);//寻找能到的最远边界
                if(i==end){//当前状态的区间的点用完了,更新区间、步数
                    step++;
                    end=maxPos;
                }
            }
        }
    return step;
    }
};

最大子序和

在这里插入图片描述

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int N=nums.size();
        
        int res=INT_MIN;
        if(N==1) return nums[0];
        int dp=-1;
        for(int i=0;i<N;i++){
            dp=max(nums[i],dp+nums[i]);
            //dp代表以i为截点
            //最新的子序列的和,两个状态的计算:不要之前子序列(子序列和小于0了)
            //和要之前子序列(还是大于0)
            res=max(res,dp);
        }
        return res;
    }
};

环状最大子序和

在这里插入图片描述

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int N=nums.size();
        int dp1=-1;
        int dp2=1;
        int res1=INT_MIN,res2=INT_MAX;
    //分三种情况,最大和的子序跨环/不跨环/序列全为负数
    //跨环就是用总和减去不跨环的最小和,全负数是因为sum-res2>=0,不可能是跨环的。
    //并且跨环可以看作是从两头出发找最大,意思是两头必包含
        int sum=0;
        for(int i=0;i<N;i++){
            sum+=nums[i];
            dp1=max(dp1+nums[i],nums[i]);
            res1=max(res1,dp1);
            dp2=min(dp2+nums[i],nums[i]);
            res2=min(dp2,res2);
        }
        if(res1<0) return res1;
        return res1>(sum-res2)?res1:(sum-res2);
    }
};

乘积最大子数组

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxProduct = function(nums) {
    let N=nums.length
    let res=nums[0]
    let dp1=1//正子数组
    let dp2=1//负子数组
    for(let i=0;i<N;i++){
        if(nums[i]<0){
            [dp1,dp2]=[dp2,dp1]//交换
        }
        dp1=Math.max(dp1*nums[i],nums[i])//最大
        dp2=Math.min(dp2*nums[i],nums[i])//最小,算最小是因为它有可能翻身变最大
        res=Math.max(res,dp1)
    }
    return res
};

乘积为正的最长子数组长度

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number}
 */
var getMaxLen = function(nums) {
    let z=0,f=0;//正的,负的
    let res=Number.MIN_VALUE
    let N=nums.length;
    for(let i=0;i<N;i++){
        if(!nums[i]){//0就都断了
            z=0
            f=0
        }else if(nums[i]<0){//正负交换,负增加,前面有正的话正也加,因为可能翻身变负
            [z,f]=[f,z]
            f++
            if(z>0) z++
        }else{//正增加,前面有负的话负也加,因为可能翻身变正
            z++
            if(f>0) f++
        }
        res=Math.max(z,res)//都是要正的最大
    }
    return res
};

单词拆分

在这里插入图片描述

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int N=s.size();
        vector<int> dp=vector<int>(N+1,0);//表示当前子串能不能拆
        dp[0]=1;
        for(int i=1;i<=N;i++){
            for(int j=0;j<i;j++){
                if(dp[j]&&find(wordDict.begin(),wordDict.end(),s.substr(j,i-j))!=wordDict.end()){
                    // 子串左边部分能并且右边也在字典里
                    dp[i]=1;//当前子串可拆
                    break;
                }
            }
        }
        return dp[N];
    }
};

接雨水

在这里插入图片描述

class Solution {
public:
    int trap(vector<int>& height) {
        int N=height.size();
        if(N==0) return 0;
        vector<int> dp1=vector<int>(N,0);//左边最大高度
        vector<int> dp2=vector<int>(N,0);//右
        dp1[0]=height[0];
        for(int i=1;i<N;i++){
            dp1[i]=max(dp1[i-1],height[i]);
                  
        }
        dp2[N-1]=height[N-1];
        for(int i=N-2;i>=0;i--){
            dp2[i]=max(dp2[i+1],height[i]);
                 
        }
        int ans=0;
        for(int i=0;i<N;i++){//左右阴影覆盖就是能装的,画图看看哈
            ans+=min(dp1[i],dp2[i])-height[i];
        }
        return ans;
    }
};

矩阵区域和

在这里插入图片描述

class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        int M=mat.size();
        int N=mat[0].size();
        vector<vector<int>> dp=vector<vector<int>>(M+1,vector<int>(N+1,0));
        vector<vector<int>> res=vector<vector<int>>(M,vector<int>(N,0));
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                dp[i + 1][j + 1] = dp[i][j + 1] + dp[i + 1][j] - dp[i][j] + mat[i][j];
            }
        }
        for(int i=0;i<M;i++){
            for(int j=0;j<N;j++){
                // 左上角坐标
                int r1 = max(i - k, 0);
                int c1 = max(j - k, 0);
                // 右下角坐标
                int r2 = min(i + k, M - 1);
                int c2 = min(j + k, N - 1);
                res[i][j] = dp[r2 + 1][c2 + 1] - dp[r1][c2 + 1] - dp[r2 + 1][c1] + dp[r1][c1];
            }
        }
        return res;
    }
};

二维区域和检索

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

class NumMatrix {
public:
    vector<vector<int>> dp;
    NumMatrix(vector<vector<int>>& matrix) {
        int N=matrix.size();
        int M=matrix[0].size();
        dp=vector<vector<int>>(N+1,vector<int>(M+1,0));
        for(int i=0;i<N;i++){
            for(int j=0;j<M;j++){
                dp[i+1][j+1]=dp[i+1][j]+dp[i][j+1]-dp[i][j]+matrix[i][j];
            }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        int res=0;
        res+=dp[row2+1][col2+1]-dp[row2+1][col1]-dp[row1][col2+1]+dp[row1][col1];
        return res;
    }
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix* obj = new NumMatrix(matrix);
 * int param_1 = obj->sumRegion(row1,col1,row2,col2);
 */

下降路径最小和

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

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int N=matrix.size();
        vector<vector<int>> dp=vector<vector<int>>(N+1,vector<int>(N+2,101));
        
        int res=INT_MAX;
        
        for(int i=0;i<=N;i++) dp[0][i]=0;
        for(int i=1;i<=N;i++){
            for(int j=1;j<=N;j++){
                dp[i][j]=matrix[i-1][j-1]+min(dp[i-1][j-1],min(dp[i-1][j],dp[i-1][j+1]));
            }
        }

        for(int i=1;i<=N;i++) res=min(res,dp[N][i]);
        
        return res;
    }
};

三角形最小路径和

在这里插入图片描述

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

丑数

在这里插入图片描述

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> dp=vector<int>(n,1);
        int d1=0,d2=0,d3=0;
        int a,b,c;
        int minDp;
        for(int i=1;i<n;i++){
            a=dp[d1]*2,b=dp[d2]*3,c=dp[d3]*5;
            minDp=min(min(a,b),c);
            if(minDp==a) d1++;
            if(minDp==b) d2++;
            if(minDp==c) d3++;
            dp[i]=minDp;
        }
        // for(int i=0;i<n;i++) cout<<dp[i]<<' ';
        return dp[n-1];
    }
};

不同的二叉搜索树

在这里插入图片描述

class Solution {
public:
    int numTrees(int n) {
        // dp[n]=dp[i]*dp[n-i]
        vector<int> dp=vector<int>(n+1,0);
        dp[0]=dp[1]=1;
        for(int i=2;i<=n;i++){
            for(int j=1;j<=i;j++){
                dp[i]+=dp[j-1]*dp[i-j];
            }     
        }
        return dp[n];
    }
};

等差数列划分

在这里插入图片描述

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        int res=0; 
        int N=nums.size();
        if(N==1) return 0;
        if(N==2) return 0;
        vector<int> dp=vector<int>(N,0);  
        for(int j=2;j<N;j++){    
                int d=nums[j-1]-nums[j-2];
                if(d==(nums[j]-nums[j-1])){
                    dp[j]++;
                    dp[j]+=dp[j-1];
                    res+=dp[j];  
                } 
        }
        return res;  
    }
};

解码方法

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

class Solution {
public:
    int numDecodings(string s) {
        int N=s.size();
        int res=0;
        vector<int> dp=vector<int>(N,0);
        if(s[0]=='0') return 0;
        else{
            dp[0]=1;
            res+=1;
        } 
        if(N==1) return res;
        if((s[0]!='0'&&s[1]!='0')&&(((s[0]-'0')*10+s[1]-'0')>0&&((s[0]-'0')*10+s[1]-'0')<27))       {
            dp[1]=2;
        }else if(s[1]=='0'&&s[0]-'0'>2){
            return 0;
        }
        else {
            dp[1]=1;
        }

        for(int i=2;i<N;i++){
            if(s[i]=='0'&&s[i-1]=='0'){
                return 0;
            }else if(s[i]=='0'&&((s[i-1]-'0')*10+s[i]-'0')>0&&((s[i-1]-'0')*10+s[i]-'0')<27){
                dp[i]=dp[i-2];   
            }else if(s[i-1]!='0'&&((s[i-1]-'0')*10+s[i]-'0')>0&&((s[i-1]-'0')*10+s[i]-'0')<27){
                dp[i]=dp[i-1]+dp[i-2];
            }else if(s[i]=='0'){
                return 0;
            }else{  
                dp[i]=dp[i-1];
            }
        }
        
        return dp[N-1];
    }
};
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值