动态规划算法设计与例题

一.斐波那契数列模型

1.第N个泰波那契数

1137. 第 N 个泰波那契数 - 力扣(LeetCode)

class Solution 
{
public:
    int tribonacci(int n) 
    {
        //防止越界,先处理边界值
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 1;

        //1.创建dp表
        vector<int> dp(n+1);   //因为dp表下标从0开始,所以要找第n个dp的值就需要dp(n+1)

        //2.初始化
        dp[0]=0;
        dp[1]=1;
        dp[2]=1;

        //3.填表
        for(int i=3;i<=n;i++)
        {
            dp[i]=dp[i-1]+dp[i-2]+dp[i-3];
        }

        //4.返回值
        return dp[n];
    }
};

class Solution 
{
public:
    int tribonacci(int n) 
    {
        //防止越界,先处理边界值
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 1;

        //1.创建滚动变量
        int a=0,b=1,c=1,d=0;

        //2.填表
        for(int i=3;i<=n;i++)
        {
            d=a+b+c;
            //3.滚动操作
            a=b;
            b=c;
            c=d;
        }

        //4.返回值
        return d;
    }
};

2. 三步问题

面试题 08.01. 三步问题 - 力扣(LeetCode)

class Solution 
{
public:
    int waysToStep(int n) 
    {
        //0.处理边界情况 
        if(n==1) return 1;
        if(n==2) return 2;
        if(n==3) return 4;
        //1.创建dp表
        vector<int> dp(n+1);
        
        //2.初始化
        dp[1]=1,dp[2]=2,dp[3]=4;

        //3.填表
        for(int i=4;i<=n;i++)
        {
            dp[i]=((dp[i-1]+dp[i-2])%1000000007+dp[i-3])%1000000007;
        }

        //4.返回值
        return dp[n];
    }
};

 3.使用最小花费爬楼梯

746. 使用最小花费爬楼梯 - 力扣(LeetCode)

解法一:i为当前位置的终点
class Solution 
{
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        //1.创建dp表
        int n=cost.size();
        vector<int> dp(n+1);
    
        //2.初始化
        dp[0]=0,dp[1]=0;

        //3.填表
        for(int i=2;i<=n;i++)
        {
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }

        //4.返回值
        return dp[n];
    }
};

解法二:i为当前位置的起点
class Solution 
{
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        //1.创建dp表
        int n=cost.size();
        vector<int> dp(n);
    
        //2.初始化
        dp[n-1]=cost[n-1],dp[n-2]=cost[n-2];

        //3.填表
        for(int i=n-3;i>=0;i--)
        {
            dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i]);
        }

        //4.返回值
        return min(dp[0],dp[1]);
    }
};

4.解码方法(再看一遍)

91. 解码方法 - 力扣(LeetCode)

class Solution 
{
public:
    int numDecodings(string s) 
    {
        //1.创建dp表
        int n=s.size();
        vector<int> dp(n);
        //2.初始化
        if(s[0] == '0') dp[0]=0;    //按单字母解码
        else dp[0]=1;

        if(n==1) return dp[0];

        if(s[0]!='0' && s[1]!='0') dp[1]++;   //按单字母解码两个字母
        int t=(s[0]-'0')*10+(s[1]-'0');
        if(t>=10 && t<=26) dp[1]++;    //按双字母解码

        //3.填表
        for(int i=2;i<n;i++)
        {
            if(s[i]!='0') dp[i] += dp[i-1];  //单字母编码能编码成功,说明前dp[i-1]种编码方式且第i位置单编码是成立的
            int t=(s[i-1]-'0')*10+(s[i]-'0');
            if(t>=10 && t<=26) dp[i] += dp[i-2]; //双字母编码能编码成功,说明前dp[i-2]种编码方式且第i位置双编码是成立的
        }

        //4.返回值
        return dp[n-1];
    }
};

二.路径问题 

 1.不同路径

62. 不同路径 - 力扣(LeetCode)

class Solution 
{
public:
    int uniquePaths(int m, int n) 
    {
        //1.创建dp表
        vector<vector<int>> dp(m+1,vector<int>(n+1));  //多加一行一列,可以省去很多的越界情况
        //2.初始化
        dp[0][1]=1;

        //3.填表
        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];
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

 2.不同路径Ⅱ

63. 不同路径 II - 力扣(LeetCode)

class Solution 
{
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) 
    {
        //1.创建dp表
        int m=obstacleGrid.size(),n=obstacleGrid[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        //2.初始化
        dp[0][1]=1;

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(obstacleGrid[i-1][j-1]==1)
                {
                    dp[i][j]=0;
                }
                else
                {
                    dp[i][j]=dp[i][j-1]+dp[i-1][j];
                }
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

3.礼物的最大价值

LCR 166. 珠宝的最高价值 - 力扣(LeetCode)

class Solution 
{
public:
    int jewelleryValue(vector<vector<int>>& frame) 
    {
        //1.建立dp表
        int m=frame.size(),n=frame[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        //2.初始化
    
        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=max(dp[i-1][j],dp[i][j-1])+frame[i-1][j-1];
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

4.下降路径最小和

931. 下降路径最小和 - 力扣(LeetCode)

class Solution 
{
public:
    int minFallingPathSum(vector<vector<int>>& matrix) 
    {
        int n=matrix.size();
        //1.建立dp表
        vector<vector<int>> dp(n+1,vector<int>(n+2,INT_MAX));
        //2.初始化
        for(int j=0;j<n+2;j++) dp[0][j]=0;

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i-1][j+1]))+matrix[i-1][j-1];
            }
        }

        //4.返回值
        int min=INT_MAX;
        for(int i=1;i<=n;i++)
        {
            if(dp[n][i] < min) min=dp[n][i];
        }
        return min;
    }
};

5.最小路径和

64. 最小路径和 - 力扣(LeetCode)

class Solution 
{
public:
    int minPathSum(vector<vector<int>>& grid) 
    {
        //1.创建dp表
        int m=grid.size(),n=grid[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
        //2.初始化
        dp[0][1]=0;

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1];
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

6.地下城游戏

174. 地下城游戏 - 力扣(LeetCode)

class Solution 
{
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) 
    {
        //1.创建dp表
        int m=dungeon.size(),n=dungeon[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX ));
        //2.初始化
        dp[m][n-1]=1,dp[m-1][n]=1;
        //3.填表
        for(int i=m-1;i>=0;i--)  //倒着填表
        {
            for(int j=n-1;j>=0;j--)
            {
                dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i][j];
                dp[i][j]=max(1,dp[i][j]);
            }
        }
        //4.返回值
        return dp[0][0];
    }
};

三.简单多状态dp问题(以i位置结尾时,有多个状态,针对每个状态,都要有一个dp方程)

1.按摩师(在i位置时有两个状态)

面试题 17.16. 按摩师 - 力扣(LeetCode)

class Solution 
{
public:
    int massage(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==0) return 0;  //处理边界情况
        vector<int> f(n);  //f表示选到i位置时,nums[i]要选,的预约最大值
        vector<int> g(n);  //g表示选到i位置时,nums[i]不选,的预约最大值
        //2.初始化
        f[0]=nums[0];
        g[0]=0;
        //3.填表
        for(int i=1;i<n;i++)
        {
            f[i]=g[i-1]+nums[i];
            g[i]=max(f[i-1],g[i-1]);
        }
        //4.返回值
        return max(f[n-1],g[n-1]);
    }
};

 2.打家劫舍(在i位置时有两个状态)

LCR 089. 打家劫舍 - 力扣(LeetCode)

class Solution 
{
public:
    int rob(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==0) return 0;
        vector<int> f(n);   //抢第i个的最大值
        vector<int> g(n);   //不强第i个的最大值
        //2.初始化
        f[0]=nums[0];
        g[0]=0;

        //3.填表
        for(int i=1;i<n;i++)
        {
            f[i]=g[i-1]+nums[i];
            g[i]=max(g[i-1],f[i-1]);
        }

        //4.返回值
        return max(f[n-1],g[n-1]);
    }
};

3.打家劫舍Ⅱ(在i位置时有两个状态)

213. 打家劫舍 II - 力扣(LeetCode)

class Solution 
{
public:
    int rob1(vector<int>& nums,int l,int r)
    {
        int len=r-l+1;
        //1.创建dp表
        vector<int> f(len);
        vector<int> g(len);
        //2.初始化
        f[0]=nums[l];
        g[0]=0;

        //3.填表
        for(int i=1;i<len;i++)
        {
            f[i]=nums[l+i]+g[i-1];
            g[i]=max(f[i-1],g[i-1]);
        }
        //4.返回值
        return max(f[len-1],g[len-1]);
    }
    int rob(vector<int>& nums) 
    {
        int n=nums.size();
        if(n==1) return nums[0];
        if(n==2) return max(nums[0],nums[1]);
        if(n==3) return max(nums[2],max(nums[0],nums[1]));
        int x=nums[0]+rob1(nums,2,n-2);  //抢第一个
        int y=rob1(nums,1,n-1);               //不抢第一个
        return max(x,y);
    }
};

4.删除并获得点数(在i位置时有两个状态)

740. 删除并获得点数 - 力扣(LeetCode)

class Solution 
{
public:
    int deleteAndEarn(vector<int>& nums) 
    {
        const int N=10001;
        //1.创建dp表
        int arr[N]={0};
        vector<int> f(N);
        vector<int> g(N);
        //2.初始化
        for(auto x:nums)
        {
            arr[x] += x; 
        }
        f[0]=arr[0];
        g[0]=0;

        //3.填表
        for(int i=1;i<N;i++)
        {
            f[i]=arr[i]+g[i-1];
            g[i]=max(f[i-1],g[i-1]);
        }

        //4.返回值
        return max(f[N-1],g[N-1]);
    }
};

5.粉刷房子(在i位置时有三个状态)

LCR 091. 粉刷房子 - 力扣(LeetCode)

class Solution 
{
public:
    int minCost(vector<vector<int>>& costs) 
    {
        //1.创建dp表
        int n=costs.size();
        int dp[100][3]={0};

        //2.初始化
        dp[0][0]=costs[0][0];
        dp[0][1]=costs[0][1];
        dp[0][2]=costs[0][2];

        //3.填表
        for(int i=1;i<n;i++)
        {
            dp[i][0]=costs[i][0]+min(dp[i-1][1],dp[i-1][2]);
            dp[i][1]=costs[i][1]+min(dp[i-1][0],dp[i-1][2]);
            dp[i][2]=costs[i][2]+min(dp[i-1][1],dp[i-1][0]);
        }

        //4.返回值
        return min(dp[n-1][0],min(dp[n-1][1],dp[n-1][2]));
    }
};

6.买卖股票最佳时期含冷冻期(在i位置时有三个状态)

309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode)

 

class Solution 
{
public:
    int maxProfit(vector<int>& prices) 
    {
        int n=prices.size();
        //1.创建dp表
        vector<vector<int>> dp(n,vector<int>(3));

        //2.初始化
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        dp[0][2]=0;

        //3.填表
        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][2]);             //可买
            dp[i][2]=dp[i-1][0]+prices[i];                   //进入冷冻期
        }

        //4.返回值
        return max(dp[n-1][0],max(dp[n-1][1],dp[n-1][2]));
    }
};

7.买卖股票的最佳时机含手续费(在i位置时有两个状态)

714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)

 

class Solution 
{
public:
    int maxProfit(vector<int>& prices, int fee) 
    {
        //1.创建dp表
        int n=prices.size();
        vector<int> f(n);
        vector<int> g(n);
        //2.初始化
        f[0]=-prices[0];
        g[0]=0;

        //3.填表
        for(int i=1;i<n;i++)
        {
            f[i]=max(f[i-1],g[i-1]-prices[i]);   //可卖
            g[i]=max(g[i-1],f[i-1]+prices[i]-fee);  //可买
        }
        //4.返回值
        return max(f[n-1],g[n-1]);
    }
};

8.买卖股票最佳时机

123. 买卖股票的最佳时机 III - 力扣(LeetCode)

class Solution 
{
public:
    const int MIN = -0x3f3f3f;
    int maxProfit(vector<int>& prices) 
    {
        //1.创建dp表
        int n=prices.size();
        vector<vector<int>> f(n,vector<int> (3,MIN));
        vector<vector<int>> g(n,vector<int> (3,MIN));
        //2.初始化
        f[0][0]=-prices[0];
        g[0][0]=0;

        //3.填表
        for(int i=1;i<n;i++)   //天数
        {
            for(int j=0;j<3;j++)  //次数
            {
                f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);

                g[i][j]=g[i-1][j]; //该状态一定存在
                if(j-1>=0)   //该状态可能存在
                    g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
            }
        }

        //4.返回值
        int Max=MIN;
        for(int x:g[n-1]) Max=max(Max,x);
        return Max;
    }
};

9.买卖股票最佳时机

188. 买卖股票的最佳时机 IV - 力扣(LeetCode)

class Solution 
{
public:
    const int MIN = -0x3f3f3f;
    int maxProfit(int k, vector<int>& prices) 
    {
        //1.创建dp表
        int n=prices.size();
        vector<vector<int>> f(n,vector<int> (k+1,MIN));
        vector<vector<int>> g(n,vector<int> (k+1,MIN));
        //2.初始化
        f[0][0]=-prices[0];
        g[0][0]=0;

        //3.填表
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=k;j++)
            {
                f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);

                g[i][j]=g[i-1][j];
                if(j-1>=0)
                    g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
            }
        }

        //4.返回值
        int Max=MIN;
        for(int x:g[n-1]) Max=max(Max,x);
        return Max;
    }
};

四.子数组系列

1.最大子数组和

53. 最大子数组和 - 力扣(LeetCode)

class Solution 
{
public:
    int maxSubArray(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==1) return nums[0];
        vector<int> dp(n);
        //2.初始化
        dp[0]=nums[0];

        //3.填表
        for(int i=1;i<n;i++)
        {
            dp[i]=max(nums[i],dp[i-1]+nums[i]);
        }

        //4.返回值
        int Max=INT_MIN;
        for(int x:dp) Max=max(Max,x);
        return Max;
    }
};

2.环形子数组的最大和

918. 环形子数组的最大和 - 力扣(LeetCode)

class Solution 
{
public:
    int maxSubarraySumCircular(vector<int>& nums) 
    {
        //1.建立dp表
        int n=nums.size(),sum=0;
        if(n==1) return nums[0];
        for(int x:nums) sum += x;
        vector<int> f(n);  //以i为结尾最大和
        vector<int> g(n);  //以i为结尾最小和
        //2.初始化
        f[0]=nums[0];   
        g[0]=nums[0];

        //3.填表
        for(int i=1;i<n;i++)
        {
            f[i]=max(nums[i]+f[i-1],nums[i]);
            g[i]=min(nums[i]+g[i-1],nums[i]);
        }

        //4.返回值
        int fmax=INT_MIN;
        int gmin=INT_MAX;
        for(int x:f) fmax=max(fmax,x);
        for(int x:g) gmin=min(gmin,x);
        return sum==gmin?fmax:max(fmax,sum-gmin);  //当全为负数的时候sum-gmin为0,是特殊情况
    }
};

3.乘积最大子数组

152. 乘积最大子数组 - 力扣(LeetCode)

class Solution 
{
public:
    int maxProduct(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==1) return nums[0];
        vector<int> f(n);   //f表用来存最大值
        vector<int> g(n);   //g表用来存最小值,最小值乘负数就成最大值了
        //2.初始化
        f[0]=nums[0];
        g[0]=nums[0];

        //3.填表
        int fmax=nums[0],gmin=nums[0];
        for(int i=1;i<n;i++)
        {
            f[i]=max(nums[i],max(f[i-1]*nums[i],g[i-1]*nums[i]));  //长度为一,nums[i]大小于0
            fmax=max(fmax,f[i]);
            g[i]=min(nums[i],min(f[i-1]*nums[i],g[i-1]*nums[i]));  //nums[i]大于0乘最小值,小于0乘最大值
        }
        
        //4.返回值
        return fmax;
    }
};

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

1567. 乘积为正数的最长子数组长度 - 力扣(LeetCode)

class Solution 
{
public:
    int getMaxLen(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        vector<int> f(n);   //f表示以i为结尾最大正数积的最大长度
        vector<int> g(n);   //g表示以i为结尾最小负数积的最大长度
        //当nums等于0时直接不用考虑,那个位置不存在使结果为正数的长度,0就放着就可以了
        //2.初始化
        if(nums[0]<0) 
        {
            f[0]=0;
            g[0]=1;
        }
        else if(nums[0]>0)
        {
            f[0]=1;
            g[0]=0;
        }
        else
        {
            f[0]=0;
            g[0]=0;
        }
        if(n==1) return f[0];

        //3.填表
        for(int i=1;i<n;i++)
        {
            if(nums[i]>0)
            {
                f[i]=f[i-1]+1;
                g[i]=(g[i-1]==0 ? 0 : g[i-1]+1);
            }
            else if(nums[i]<0)
            {
                f[i]=(g[i-1]==0 ? 0 : g[i-1]+1);
                g[i]=f[i-1]+1;
            }
        }

        //4.返回值
        int fmax=INT_MIN;
        for(int x:f) fmax=max(fmax,x);
        return fmax;
    }
};

5.等差数列划分

413. 等差数列划分 - 力扣(LeetCode)

class Solution 
{
public:
    int numberOfArithmeticSlices(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==1 || n==2) return 0;
        vector<int> dp(n);
        //2.初始化
        dp[0]=0;
        dp[1]=0;

        //3.填表
        int sum=0;
        for(int i=2;i<n;i++)
        {
            int a=nums[i-2],b=nums[i-1],c=nums[i];
            if(a-b == b-c) 
            {
                dp[i]=dp[i-1]+1;
            }
            else
            {
                dp[i]=0;
            }
            sum += dp[i];
        }

        //4.返回值
        return sum;
    }
};

6.最长湍流子数组

978. 最长湍流子数组 - 力扣(LeetCode)

class Solution 
{
public:
    int maxTurbulenceSize(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==1) return 1;
        vector<int> f(n,1);      //表示以i为结尾,并且呈现上升趋势的最大子数组长度
        vector<int> g(n,1);      //表示以i为结尾,并且呈现下降趋势的最大子数组长度
        //2.初始化

        //3.填表
        int fmax=INT_MIN,gmax=INT_MIN;
        for(int i=1;i<n;i++)
        {
            if(nums[i-1]<nums[i]) f[i]=g[i-1]+1;
            if(nums[i-1]>nums[i]) g[i]=f[i-1]+1;
            fmax=max(f[i],fmax);
            gmax=max(g[i],gmax);
        }

        //4.返回值
        return max(fmax,gmax);
    }
};

7.单词拆分

139. 单词拆分 - 力扣(LeetCode)

class Solution 
{
public:
    bool wordBreak(string s, vector<string>& wordDict) 
    {
        //优化
        unordered_set<string> hash;
        for(auto x:wordDict) hash.insert(x);

        //1.创建dp表
        int n=s.size();
        vector<bool> dp(n+1);    //这次用的bool类型的dp表
        //2.初始化 
        dp[0]=true;   //保证后续填表正确
        s=' '+s;     //使原始字符串下标统一加一

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j>=1;j--)   //最后一个单词的起始位置
            {
                if(dp[j-1] && hash.count(s.substr(j,i-j+1)))   //如果满足前i-1在字典中,最后一个单词在字典中
                {
                    dp[i]=true;
                    break;
                }
            }
        }

        //4.返回值
        return dp[n];
    }
};

8.环绕字符串中唯⼀的⼦字符串

467. 环绕字符串中唯一的子字符串 - 力扣(LeetCode)

class Solution 
{
public:
    int findSubstringInWraproundString(string s) 
    {
        //1.创建dp表
        int n=s.size();
        vector<int> dp(n,1);
        //2.初始化

        //3.填表
        for(int i=1;i<n;i++)
        {
            if(s[i]==s[i-1]+1 || (s[i-1]=='z'&&s[i]=='a'))
            {
                dp[i] += dp[i-1];
            }
        }
        //4.返回值
        int hash[26]={0};
        for(int i=0;i<n;i++)
        {
            hash[s[i]-'a'] = max(hash[s[i]-'a'],dp[i]);  //dp[i]对应的是哪个字符没有影响,只要有hash[s[i]-'a']限制在26个字母范围内就行
        }
        int sum=0;
        for(auto x:hash) sum += x;
        return sum;
    }
};

五.子序列问题

1.最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode)

class Solution 
{
public:
    int lengthOfLIS(vector<int>& nums) 
    {
        //1.创建dp表
        int n = nums.size();
        if(n==1) return 1;
        vector<int> dp(n,1);

        //2.初始化

        //3.填表
        int ans = 0;
        for (int i = 1; i < n; i++)
        {
            for (int j = 0; j < i; j++)
            {
                if (nums[j] < nums[i])
                {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            ans = max(ans, dp[i]);
        }
        //4.返回值
        return ans;
    }
};

2.摆动序列

376. 摆动序列 - 力扣(LeetCode)

class Solution 
{
public:
    int wiggleMaxLength(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        if(n==1) return 1;
        vector<int> f(n,1);
        vector<int> g(n,1);

        //2.初始化

        //3.填表
        int fmax=INT_MIN,gmax=INT_MIN;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(nums[i]>nums[j])
                {
                    f[i]=max(f[i],g[j]+1);
                }
                if(nums[i]<nums[j])
                {
                    g[i]=max(g[i],f[j]+1);
                }
            }
            fmax=max(fmax,f[i]);
            gmax=max(gmax,g[i]);
        }

        //4.返回值
        return max(fmax,gmax);
    }
};

3.最长递增子序列的个数

673. 最长递增子序列的个数 - 力扣(LeetCode)

 

class Solution 
{
public:
    int findNumberOfLIS(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        vector<int> len(n,1),count(n,1);

        //2.初始化

        //3.填表
        int maxlen=1,maxcount=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(nums[j]<nums[i])  //保证递增
                {
                    if(len[j]+1 == len[i]) count[i] += count[j];
                    else if(len[j]+1 > len[i]) 
                    {
                        len[i]=len[j]+1;
                        count[i]=count[j];  
                    }
                }
            }
            if(maxlen==len[i]) maxcount += count[i];
            else if(maxlen < len[i])
            {
                maxlen = len[i];   //改变最大值
                maxcount = count[i];   //并且重新计数
            }
        }

        //4.返回值
        return maxcount;
    }
};

4.最长数对链

646. 最长数对链 - 力扣(LeetCode)

class Solution 
{
public:
    int findLongestChain(vector<vector<int>>& pairs) 
    {
        //预处理
        sort(pairs.begin(),pairs.end());

        //1.创建dp表
        int n=pairs.size();
        vector<int> dp(n,1);

        //2.初始化

        //3.填表
        int maxdp=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(pairs[j][1] < pairs[i][0]) //要先保证后面的数对的第一个值大于前一个数对第二个值
                {
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            maxdp=max(maxdp,dp[i]);
        }

        //4.返回值
        return maxdp;
    }
};

5.最长定差子序列

1218. 最长定差子序列 - 力扣(LeetCode)

按照之前的解法:超出时间限制

class Solution 
{
public:
    int longestSubsequence(vector<int>& arr, int difference) 
    {
        //1.创建dp表
        int n=arr.size();
        if(n==1) return 1;
        vector<int> dp(n,1);

        //2.初始化

        //3.填表
        int maxdp=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(arr[i]-arr[j]==difference)
                {
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            maxdp=max(maxdp,dp[i]);
        }

        //4.返回值
        return maxdp;
    }
};

class Solution 
{
public:
    int longestSubsequence(vector<int>& arr, int difference) 
    {
        //1.创建dp表
        int n=arr.size();
        if(n==1) return 1;
        unordered_map<int,int> hash;
        
        //2.初始化
        hash[arr[0]]=1;

        //3.填表
        int maxdp=1;
        for(int i=1;i<n;i++)
        {
            hash[arr[i]]=hash[arr[i]-difference]+1;
            maxdp=max(maxdp,hash[arr[i]]);
        }

        //4.返回值
        return maxdp;
    }
};

6.最长斐波那契子序列的长度

873. 最长的斐波那契子序列的长度 - 力扣(LeetCode)

class Solution 
{
public:
    int lenLongestFibSubseq(vector<int>& arr) 
    {
        //1.创建dp表
        int n=arr.size();
        unordered_map<int,int> hash;
        for(int i=0;i<n;i++) hash[arr[i]]=i;
        vector<vector<int>> dp(n,vector<int>(n,2));  //创建2维dp表,靠ij来固定住k

        //2.初始化

        //3.填表
        int dpmax=2;
        for(int j=2;j<n;j++)  //固定c
        {
            for(int i=1;i<j;i++)  //固定b
            {
                int c=arr[j],b=arr[i],a=arr[j]-arr[i];
                if(a<arr[i] && hash.count(a))   //a存在,而且比b小
                {
                    int k=hash[a];  //k为a对应的下标
                    dp[i][j]=dp[k][i]+1;                    
                }
                dpmax=max(dpmax,dp[i][j]);
            }
        }

        //4.返回值
        return dpmax<3 ? 0 :dpmax;
    }
};

7.最长等差数列

1027. 最长等差数列 - 力扣(LeetCode)

class Solution 
{
public:
    int longestArithSeqLength(vector<int>& nums) 
    {
        //1.创建dp表
        unordered_map<int,int> hash;  //元素和下标对应起来
        hash[nums[0]]=0;
        int n=nums.size();
        vector<vector<int>> dp(n,vector<int>(n,2));
        //2.初始化

        //3.填表
        int dpmax=2;
        for(int i=1;i<n;i++) //固定倒数第二个数,枚举最后一个数
        {
            for(int j=i+1;j<n;j++)
            {
                int b=nums[i],c=nums[j];
                int a=2*b-c;
                if(hash.count(a)) 
                {
                    int k=hash[a];
                    dp[i][j]=dp[k][i]+1;
                }
                dpmax=max(dpmax,dp[i][j]);
            }
            hash[nums[i]]=i;   //边走边填,减少hash.count()的时间消耗
        }
        //4.返回值
        return dpmax;
    }
};

8.等差序列划分II

446. 等差数列划分 II - 子序列 - 力扣(LeetCode)

class Solution 
{
public:
    int numberOfArithmeticSlices(vector<int>& nums) 
    {
        int n=nums.size();
        //优化
        unordered_map<long long,vector<int>> hash;   //存元素与对应的下标数组 
        for(int i=0;i<n;i++)
        {
            hash[nums[i]].push_back(i);
        }
        //1.创建dp表
        vector<vector<int>> dp(n,vector<int>(n,0));
        
        //2.初始化

        //3.填表
        int sum=0;
        for(int j=2;j<n;j++)
        {
            for(int i=1;i<j;i++)
            {
                long long a=(long long)2*nums[i]-nums[j];
                if(hash.count(a))
                {
                    for(auto k:hash[a])
                    {
                        if(k<i) dp[i][j] += dp[k][i]+1;
                    }
                }
                sum += dp[i][j];
            }
        }
        //4.返回值
        return sum;
    }
};

六.回文串问题

1.回文子串

647. 回文子串 - 力扣(LeetCode)

class Solution 
{
public:
    int countSubstrings(string s) 
    {
        //1.创建dp表
        int n=s.size();
        vector<vector<bool>> dp(n,vector<bool>(n,false));

        //2.初始化

        //3.填表
        int sum=0;
        for(int j=0;j<n;j++)
        {
            for(int i=0;i<=j;i++)
            {
                if(s[i]==s[j])
                {
                    if(i==j) dp[i][j]=true;
                    else if(i+1==j) dp[i][j]=true;
                    else
                    {
                        dp[i][j]=dp[i+1][j-1];
                    }
                }
                if(dp[i][j]) sum++;
            }
        }

        //4.返回值
        return sum;
    }
};

2.最长回文子串

5. 最长回文子串 - 力扣(LeetCode)

class Solution 
{
public:
    string longestPalindrome(string s) 
    {
        //1.创建dp表
        int n=s.size();
        if(n==1) return s;
        vector<vector<bool>> dp(n,vector<bool>(n,false));
        //2.初始化

        //3.填表
        int maxlen=0,maxi=0;
        for(int j=0;j<n;j++)
        {
            for(int i=0;i<=j;i++)
            {
                if(s[i]==s[j]) 
                {
                    if(i==j) dp[i][j]=true;
                    else if(i+1==j) dp[i][j]=true;
                    else
                    {
                        dp[i][j]=dp[i+1][j-1];
                    }
                }
                if(dp[i][j] && (j-i+1>maxlen))
                {
                    maxlen=j-i+1;
                    maxi=i;
                } 
            }
        }

        //4.返回值
        return s.substr(maxi,maxlen);
    }
};

3.回文串分割IV

1745. 分割回文串 IV - 力扣(LeetCode)

class Solution 
{
public:
    bool checkPartitioning(string s) 
    {
        int n=s.size();
        //1.创建dp表
        vector<vector<bool>> dp(n,vector<bool>(n,false));

        //2.初始化

        //3.填表
        for(int j=0;j<n;j++)
        {
            for(int i=0;i<=j;i++)
            {
                if(s[i]==s[j])
                {
                    if(i==j) dp[i][j]=true;
                    else if(i+1==j) dp[i][j]=true;
                    else
                    {
                        dp[i][j]=dp[i+1][j-1];
                    }
                }
            }
        }

        //4.返回值
        for(int i=0;i<n;i++)
        {
            for(int j=i+2;j<n;j++)
            {
                //分成了三段:[0,i][i+1,j-1][j,n-1]
                if(dp[0][i] && dp[i+1][j-1] && dp[j][n-1]) return true;
            }
        }
    return false;
    }
};

4.回文串分割II

132. 分割回文串 II - 力扣(LeetCode)

class Solution 
{
public:
    int minCut(string s) 
    {
        //预处理
        int n=s.size();
        vector<vector<bool>> isback(n,vector<bool>(n,false));
        for(int j=0;j<n;j++)
        {
            for(int i=0;i<=j;i++)
            {
                if(s[i]==s[j])
                {
                    if(i==j) isback[i][j]=true;
                    else if(i+1==j) isback[i][j]=true;
                    else isback[i][j]=isback[i+1][j-1];
                }
            }
        }

        //1.创建dp表
        vector<int> dp(n,INT_MAX);

        //2.初始化

        //3.填表
        for(int i=0;i<n;i++)
        {
            if(isback[0][i]) dp[i]=0;  //0到i是回文串,不需要切了
            else
            {
                for(int j=0;j<=i;j++)
                {
                    if(isback[j][i])    //如果j到i是回文串
                    {
                        dp[i]=min(dp[i],dp[j-1]+1);
                    }
                }
            }
        }

        //4.返回值
        return dp[n-1];
    }
};

5.最长回文子序列

516. 最长回文子序列 - 力扣(LeetCode)

class Solution 
{
public:
    int longestPalindromeSubseq(string s) 
    {
        //1.创建dp表
        int n=s.size();
        vector<vector<int>> dp(n,vector<int>(n,0));  //dp[i][j]表示ij区间内的最长回文串序列的长度
        //2.初始化

        //3.填表
        for(int j=0;j<n;j++)
        {
            for(int i=j;i>=0;i--)
            {
                if(s[i]==s[j])
                {
                    if(i==j) dp[i][j]=1;
                    else if(i+1==j) dp[i][j]=2;
                    else dp[i][j]=dp[i+1][j-1]+2;
                }
                else
                {
                    dp[i][j]=max(dp[i][j-1],dp[i+1][j]);   //因为填dp[i][j]需要用到dp[i+1][j](下面那一行的数),所以从下往上填表
                }
            }
        }

        //4.返回值
        return dp[0][n-1];
    }
};

6.让字符串成为回⽂串的最⼩插⼊次数

1312. 让字符串成为回文串的最少插入次数 - 力扣(LeetCode)

class Solution 
{
public:
    int minInsertions(string s) 
    {
        //1.创建dp表
        int n=s.size();
        vector<vector<int>> dp(n,vector<int>(n,0));  //表示ij区间内的最少次数

        //2.初始化

        //3.填表
        for(int j=0;j<n;j++)
        {
            for(int i=j;i>=0;i--)
            {
                if(s[i]==s[j])
                {
                    if(i==j) dp[i][j]=0;
                    else if(i+1==j) dp[i][j]=0;
                    else dp[i][j]=dp[i+1][j-1];
                }
                else
                {
                    dp[i][j]=min(1+dp[i][j-1],1+dp[i+1][j]);  //最小切割次数
                }
            }
        }

        //4.返回值
        return dp[0][n-1];
    }
};

七.两个数组的 dp 问题

1.最长公共子序列

1143. 最长公共子序列 - 力扣(LeetCode)

class Solution 
{
public:
    int longestCommonSubsequence(string text1, string text2) 
    {
        //1.创建dp表
        int m=text1.size(),n=text2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));  //表示[0,i]与[0,j]区段上的最长公共子序列长度
        //2.初始化
        text1=" "+text1;
        text2=" "+text2;

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(text1[i]==text2[j])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
                }
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

2.不相交的线

1035. 不相交的线 - 力扣(LeetCode)

        类似于最长公共子序列

class Solution 
{
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) 
    {
        //1.创建dp表
        int m=nums1.size(),n=nums2.size();  
        vector<vector<int>> dp(m+1,vector<int>(n+1));  //表示的是[0,i]与[0,j]的最长公共子序列长度
        //2.初始化

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(nums1[i-1]==nums2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

3.不同的子序列

115. 不同的子序列 - 力扣(LeetCode)

class Solution 
{
public:
    int numDistinct(string s, string t) 
    {
        //1.创建dp表
        int m=t.size(),n=s.size();
        vector<vector<double>> dp(m+1,vector<double>(n+1));  //表示s的0到j区间内的所有子序列,t的0到i区间内,子串的个数

        //2.初始化
        for(int j=0;j<n;j++) dp[0][j]=1;  //当t为空串时,s中一定有一个子串
        for(int i=1;i<m;i++) dp[i][0]=0;  //当s为空串时且t不为空串,一定没有子串

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                dp[i][j] += dp[i][j-1];
                if(s[j-1]==t[i-1])
                {
                    dp[i][j] += dp[i-1][j-1];
                }
            }

        }
        //4.返回值
        return dp[m][n];
    }
};

4.通配符匹配

44. 通配符匹配 - 力扣(LeetCode)

class Solution 
{
public:
    bool isMatch(string s, string p) 
    {
        //1.创建dp表
        int m=s.size(),n=p.size();
        s=" "+s;
        p=" "+p;
        vector<vector<bool>> dp(m+1,vector<bool>(n+1,false)); //表示s的0-i能不能与p的0-j匹配上

        //2.初始化
        dp[0][0]=true;
        for(int j=1;j<=n;j++) 
        {
            if(p[j]=='*') dp[0][j]=true;
            else break;
        }

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(p[j]=='*') 
                {
                    dp[i][j]=(dp[i-1][j] || dp[i][j-1]);
                }
                else
                {
                    dp[i][j]=(p[j]==s[i] || p[j]=='?') && (dp[i-1][j-1]);
                }
            }
        }

        //4.返回值
        return dp[m][n];
    }
};

5.正则表达式匹配

10. 正则表达式匹配 - 力扣(LeetCode)

6.交错字符串

97. 交错字符串 - 力扣(LeetCode)

class Solution 
{
public:
    bool isInterleave(string s1, string s2, string s3) 
    {
        //1.创建dp表
        int m=s1.size(),n=s2.size(),s=s3.size();
        if(s!=m+n) return false;
        vector<vector<bool>> dp(m+1,vector<bool>(n+1,false));  //dp表示s1从1-i,s2从1-j,能不能构成s3的1-(i+j)

        //2.初始化
        s1=" "+s1,s2=" "+s2,s3=" "+s3;
        if(s==0) dp[0][0]=true;
        for(int j=1;j<=n;j++) 
        {
            if(s2[j]==s3[j]) dp[0][j]=true;
            else break;
        }
        for(int i=1;i<=m;i++) 
        {
            if(s1[i]==s3[i]) dp[i][0]=true;
            else break;
        }

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(s1[i]==s3[i+j] && dp[i-1][j]) dp[i][j]=true;
                else if(s2[j]==s3[i+j] && dp[i][j-1]) dp[i][j]=true;
            }
        }
        //4.返回值
        return dp[m][n];
    }
};

7.两个字符串的最⼩ ASCII 删除和

712. 两个字符串的最小ASCII删除和 - 力扣(LeetCode)

class Solution 
{
public:
    int minimumDeleteSum(string s1, string s2) 
    {
        //1.创建dp表
        int m=s1.size(),n=s2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));  //表示s1的1-i与s2的1-j的最长公共子序列的最大acsii码和

        //2.初始化

        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                if(s1[i-1]==s2[j-1])
                {
                    dp[i][j]=max(dp[i][j],dp[i-1][j-1]+s1[i-1]);
                }
            }
        }

        //4.返回值
        int sum=0;
        for(auto x:s1) sum += x;
        for(auto x:s2) sum += x;
        return sum-dp[m][n]*2;
    }
};

8.最长重复子数组

718. 最长重复子数组 - 力扣(LeetCode)

class Solution 
{
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) 
    {
        //1.创建dp表
        int m=nums1.size(),n=nums2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));  //表示num1以i结尾,num2以j结尾的所有子数组的最大公共长度

        //2.初始化

        //3.填表
        int dpmax=0;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(nums1[i-1]==nums2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                dpmax=max(dpmax,dp[i][j]);
            }
        }

        //4.返回值
        return dpmax;
    }
};

八.01背包问题

1.01背包

#include <iostream>
#include <string.h>
using namespace std;

const int N=1001;
int n,V;
int v[N],w[N];
int dp[N][N];

int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];

    //第一题  dp表示从前i个物品挑选,总体积不超过j,选出的最大价值
    dp[N][N]={0};
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=V;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j-v[i]>=0) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
        }
    }
    cout<<dp[n][V]<<endl;


    //第一题  dp表示从前i个物品挑选,总体积等于j,选出的最大价值
    memset(dp,0,sizeof(dp));
    for(int j=1;j<=V;j++) dp[0][j]=-1;  //初始化
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=V;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j-v[i]>=0 && dp[i-1][j-v[i]]!=-1) 
            dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
        }
    }
    cout<<(dp[n][V]==-1 ? 0 : dp[n][V])<<endl;
    return 0;
}

#include <iostream>
#include <string.h>
using namespace std;

const int N=1001;
int n,V;
int v[N],w[N];
int dp[N];

int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];

    //第一题  dp表示从前i个物品挑选,总体积不超过j,选出的最大价值
    dp[N]={0};
    for(int i=1;i<=n;i++)
    {
        for(int j=V;j-v[i]>=0;j--)
        {
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    cout<<dp[V]<<endl;


    //第一题  dp表示从前i个物品挑选,总体积等于j,选出的最大价值
    memset(dp,0,sizeof(dp));
    for(int j=1;j<=V;j++) dp[0][j]=-1;  //初始化
    for(int i=1;i<=n;i++)
    {
        for(int j=V;j-v[i]>=0;j--)
        {
            if(dp[i-1][j-v[i]]!=-1) 
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    cout<<(dp[V]==-1 ? 0 : dp[V])<<endl;
    return 0;
}

2.分割等和子集

416. 分割等和子集 - 力扣(LeetCode)

class Solution 
{
public:
    bool canPartition(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        int sum=0;
        for(auto x:nums) sum += x;
        if(sum%2 == 1) return false;
        int aim=sum/2;      //注意dp的长度是aim+1,不是n+1
        vector<vector<bool>> dp(n+1,vector<bool>(aim+1,false)); //表示前i个数,能否凑成和为j

        //2.初始化
        for(int i=0;i<=n;i++) dp[i][0]=true;
        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=aim;j++)  //注意dp的长度是
            {
                dp[i][j]=dp[i-1][j];
                if(j-nums[i-1]>=0) dp[i][j]= dp[i][j] || dp[i-1][j-nums[i-1]];  //注意nums[i-1],与原数组的对应关系
            }
        }
        //4.返回值
        return dp[n][sum/2];
    }
};

优化:去掉行遍历,只留一行 ; 注意从右往左遍历

class Solution 
{
public:
    bool canPartition(vector<int>& nums) 
    {
        //1.创建dp表
        int n=nums.size();
        int sum=0;
        for(auto x:nums) sum += x;
        if(sum%2 == 1) return false;
        int aim=sum/2;      
        vector<bool> dp(aim+1); //表示前i个数,能否凑成和为j

        //2.初始化
        dp[0]=true;
        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=aim;j>=nums[i-1];j--)  //注意dp的长度是
            {
                dp[j] = dp[j] || dp[j-nums[i-1]];  //注意nums[i-1],与原数组的对应关系
            }
        }
        //4.返回值
        return dp[aim];
    }
};

3.目标和

494. 目标和 - 力扣(LeetCode)

class Solution 
{
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        //预处理
        int sum=0,a=0;
        for(auto x:nums) sum += x;
        a=(sum+target)/2;
        if(a<0 || (sum+target)%2!=0) return 0;
        //1.创建dp表
        int n=nums.size();
        vector<vector<int>> dp(n+1,vector<int>(a+1));  //表示从0-i,和为j,共有几种选法

        //2.初始化
        dp[0][0]=1;

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=a;j++)   //因为第一例没初始化,所以j要从0开始
            {
                dp[i][j] += dp[i-1][j];
                if(j-nums[i-1]>=0) dp[i][j] += dp[i-1][j-nums[i-1]];   //注意多加了一行一列,要减一
            }
        }

        //4.返回值
        return dp[n][a];
    }
};


优化
class Solution 
{
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        //预处理
        int sum=0,a=0;
        for(auto x:nums) sum += x;
        a=(sum+target)/2;
        if(a<0 || (sum+target)%2!=0) return 0;
        //1.创建dp表
        int n=nums.size();
        vector<int> dp(a+1); 

        //2.初始化
        dp[0]=1;

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=a;j>=nums[i-1];j--)   //因为第一例没初始化,所以j要从0开始
            {
                dp[j] += dp[j-nums[i-1]];   
            }
        }

        //4.返回值
        return dp[a];
    }
};

4.最后一块石头的重量II

1049. 最后一块石头的重量 II - 力扣(LeetCode)

class Solution 
{
public:
    int lastStoneWeightII(vector<int>& nums) 
    {
        //1.创建dp表
        int sum=0;
        for(auto x:nums) sum += x;
        int n=nums.size();
        int aim=sum/2;
        vector<vector<int>> dp(n+1,vector<int>(aim+1));  //表示从0-i,总和不超过j,选的最大值

        //2.初始化

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=aim;j++)
            {
                dp[i][j]=dp[i-1][j];
                if(j-nums[i-1]>=0) dp[i][j]=max(dp[i][j],dp[i-1][j-nums[i-1]]+nums[i-1]);
            }
        }

        //4.返回值
        int positive = dp[n][aim];
        int negative = sum-dp[n][aim];
        return abs(negative-positive);
    }
};

九.完全背包

1.完全背包


#include<iostream>
#include<string.h>
using namespace std;

const int N=1001;
int n,V,v[N],w[N];
int dp[N][N];

int main() 
{
    cin>>n>>V;
    for(int i=1;i<=N;i++) cin>>v[i]>>w[i];

    //第一问
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=V;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);  //完全背包这里是dp[i][j-v[i]]
        }
    }
    cout<<dp[n][V]<<endl;

    //第二问
    memset(dp,0,sizeof(dp));
    for(int j=0;j<=V;j++) dp[0][j]=-1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=V;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=v[i] && dp[i][j-v[i]]!=-1) dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
        }
    }
    if(dp[n][V] == -1) cout<<0<<endl;
    else cout<< dp[n][V]<<endl;
    return 0;
}

 

int main() 
{
    cin>>n>>V;
    for(int i=1;i<=N;i++) cin>>v[i]>>w[i];

    //第一问
    for(int i=1;i<=n;i++)
    {
        for(int j=v[i];j<=V;j++)
        {
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);  //完全背包这里是dp[i][j-v[i]]
        }
    }
    cout<<dp[V]<<endl;

    //第二问
    memset(dp,0,sizeof(dp));
    for(int j=0;j<=V;j++) dp[j]=-1;
    for(int i=1;i<=n;i++)
    {
        for(int j==v[i];j<=V;j++)
        {
            if(dp[j-v[i]]!=-1) dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    if(dp[V] == -1) cout<<0<<endl;
    else cout<< dp[V]<<endl;
    return 0;
}

2.零钱兑换

322. 零钱兑换 - 力扣(LeetCode)

class Solution 
{
public:
    int coinChange(vector<int>& coins, int amount) 
    {
        const int Max = 0x3f3f3f3f;
        //1.创建dp表
        int n=coins.size();
        vector<vector<int>> dp(n+1,vector<int>(amount+1));

        //2.初始化
        for(int j=1;j<=amount;j++) dp[0][j]=Max;

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=amount;j++)
            {
                dp[i][j]=dp[i-1][j];
                if(j-coins[i-1]>=0) dp[i][j]=min(dp[i][j],dp[i][j-coins[i-1]]+1);
            }
        }

        //4.返回值
        return (dp[n][amount] >= Max ? -1 : dp[n][amount]);

    }
};

3.零钱兑换II

518. 零钱兑换 II - 力扣(LeetCode)

class Solution 
{
public:
    int change(int amount, vector<int>& coins) 
    {
        int n=coins.size();
        //1.创建dp表
        vector<vector<int>> dp(n+1,vector<int>(amount+1));

        //2.初始化
        dp[0][0]=1;

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=amount;j++)
            {
                dp[i][j] += dp[i-1][j];
                if(j-coins[i-1]>=0) dp[i][j] += dp[i][j-coins[i-1]];
            }
        }

        //4.返回值
        return dp[n][amount]==-1 ? 0 : dp[n][amount];     
    }
};

4.完全平方数

279. 完全平方数 - 力扣(LeetCode)

class Solution 
{
public:
    int numSquares(int n) 
    {
        const int Max=0x3f3f3f3f;
        vector<int> nums;
        int k=0;
        while(k*k<=n)
        {
            nums.push_back(k*k);
            k++;
        }
        int len=nums.size();

        //1.创建dp表
        vector<vector<int>> dp(len+1,vector<int>(n+1));

        //2.初始化
        for(int j=1;j<=n;j++) dp[0][j]=Max;

        //3.填表
        for(int i=1;i<=len;i++)
        {
            for(int j=0;j<=n;j++)
            {
                dp[i][j]=dp[i-1][j];
                if(j-nums[i-1]>=0) dp[i][j]=min(dp[i][j],dp[i][j-nums[i-1]]+1);
            }
        }

        //4.返回值
        return dp[len][n];
    }
};

十.二维费用的背包问题(多了一个限制)

1.一和零

474. 一和零 - 力扣(LeetCode)

class Solution 
{
public:
    int findMaxForm(vector<string>& strs, int m, int n) 
    {
        //1.创建dp表
        int len=strs.size();
        vector<vector<vector<int>>> dp(len+1,vector<vector<int>>(m+1,vector<int>(n+1)));
        //2.初始化

        //3.填表
        for(int i=1;i<=len;i++)
        {
            //统计一下字符串中01的个数
            int a=0,b=0;
            for(auto ch:strs[i-1])  //别忘了下标与原数组的对应关系
            {
                if(ch=='0') a++;
                else b++;
            }

            for(int j=0;j<=m;j++)
            {
                for(int k=0;k<=n;k++)
                {
                    dp[i][j][k]=dp[i-1][j][k];
                    if(j-a>=0 && k-b>=0) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-a][k-b]+1);
                }
            }
        }
        //4.返回值
        return dp[len][m][n];
    }
};

类比01背包的优化
 

class Solution 
{
public:
    int findMaxForm(vector<string>& strs, int m, int n) 
    {
        //1.创建dp表
        int len=strs.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        //2.初始化

        //3.填表
        for(int i=1;i<=len;i++)
        {
            //统计一下字符串中01的个数
            int a=0,b=0;
            for(auto ch:strs[i-1])  //别忘了下标与原数组的对应关系
            {
                if(ch=='0') a++;
                else b++;
            }

            for(int j=m;j>=a;j--)   //遍历顺序也要改
            {
                for(int k=n;k>=b;k--)
                {
                    dp[j][k]=max(dp[j][k],dp[j-a][k-b]+1);
                }
            }
        }
        //4.返回值
        return dp[m][n];
    }
};

2.盈利计划

879. 盈利计划 - 力扣(LeetCode)

class Solution 
{
public:
    int profitableSchemes(int n, int m, vector<int>& group, vector<int>& profit) 
    {   
        const int MOD=1e9+7;
        //1.创建dp表
        int len=group.size();
        vector<vector<vector<int>>> dp(len+1,vector<vector<int>>(n+1,vector<int>(m+1)));

        //2.初始化
        for(int j=0;j<=n;j++) dp[0][j][0]=1;

        //3.填表
        for(int i=1;i<=len;i++)
        {
            for(int j=0;j<=n;j++)
            {
                for(int k=0;k<=m;k++)
                {
                    dp[i][j][k] += dp[i-1][j][k];
                    if(j-group[i-1]>=0) dp[i][j][k] += dp[i-1][j-group[i-1]][max(k-profit[i-1],0)];
                    dp[i][j][k] %= MOD;
                }
            }
        }

        //4.返回值
        return dp[len][n][m];
    }
};

十一.似包非包

1.组合总和IV

LCR 104. 组合总和 Ⅳ - 力扣(LeetCode)

class Solution 
{
public:
    int combinationSum4(vector<int>& nums, int target) 
    {
        //1.创建dp表
        vector<double> dp(target+1);  //dp表示凑成i这个元素,需要的排列数个数

        //2.初始化
        dp[0]=1;

        //3.填表
        for(int i=1;i<=target;i++)
        {
            for(auto x:nums)
            {
                if(i-x>=0) dp[i] += dp[i-x];
            }
        }

        //4.返回值
        return dp[target];
    }
};

2.不同的二叉搜索树

96. 不同的二叉搜索树 - 力扣(LeetCode)

class Solution 
{
public:
    int numTrees(int n) 
    {
        //1.创建dp表
        vector<int> dp(n+1);  //表示一组中节点个数为i时,共有多少种组合

        //2.初始化
        dp[0]=1;

        //3.填表
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                dp[i] += dp[j-1]*dp[i-j];  
            }
        }

        //4.返回值
        return dp[n];
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

对玛导至昏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值