DP:二维费用背包问题+似包非包

二维费用的背包问题:大多以01背包为基础,存在两个限制条件!

一、一和零

. - 力扣(LeetCode)

class Solution {
public:
//需要满足两个条件的我们称之为二位费用的背包问题
    int findMaxForm(vector<string>& strs, int m, int n) {
         //dp[i][j][k]表示前i个字符串中选  0不超过j  1不超过k
         //str[i-1]有a个0,b个1  
         //如果不选i  dp[i][j][k]=dp[i-1][j][k]
         //如果选i dp[i][j][k]=dp[i-1][j-a][k-b]+1 
         int len=strs.size();
         vector<vector<vector<int>>> dp(len+1,vector<vector<int>>(m+1,vector<int>(n+1)));
         for(int i=1;i<=len;++i)
          {
             //先统计一下其数量
             int a=0,b=0;
             for(auto&ch:strs[i-1])
             if(ch=='0') ++a; else ++b;
             for(int j=0;j<=m;++j)  //只要保证i是从小到大的即可 j和k无所谓 因为会用到的是i-1那一面的值
               for(int k=0;k<=n;++k)
                {
                    dp[i][j][k]=dp[i-1][j][k];
                    if(j>=a&&k>=b) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-a][k-b]+1);
                }
          }
          return dp[len][m][n];
    }
};

 滚动数组优化一个维度

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
          //dp[i][j][k]表示前i个字符串中选  0不超过j  1不超过k
         //str[i-1]有a个0,b个1  
         //如果不选i  dp[i][j][k]=dp[i-1][j][k]
         //如果选i dp[i][j][k]=dp[i-1][j-a][k-b]+1 
         int len=strs.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
         for(int i=1;i<=len;++i)
          {
             //先统计一下其数量
             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);
          }
          return dp[m][n];
    }
};

 二、盈利计划(非常经典)

. - 力扣(LeetCode)

class Solution {
public:
    int profitableSchemes(int n, int m, vector<int>& g, vector<int>& p) {
        //01背包问题 一个工作可以选择做或者不做profit  两个限制条件 一个是minProfit 一个是n
        //dp[i][j][k] 从前i个工作中选 人数要不超过j 利润至少为k 的所有选择计划。
        const int MOD=1e9+7;
        int len=g.size();
        vector<vector<vector<int>>> dp(len+1,vector<vector<int>>(n+1,vector<int>(m+1)));
        //如果不做i工作 dp[i][j][k]=dp[i-1][j][k]
        //如果做了i工作 dp[i][j][k]+=dp[i-1][j-g[i-1]][k-p[i-1]]
        //分析初始化 如果i为0时 显然都为0 p为0时 有j=1
        for(int j=0;j<=n;++j) dp[0][j][0]=1; //完成初始化 只需考虑i为0的情况即可 因为其他都会特判
        //开始填表
        for(int i=1;i<=len;++i) //只需考虑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>=g[i-1]) dp[i][j][k]+=dp[i-1][j-g[i-1]][max(k-p[i-1],0)];
                dp[i][j][k]%=MOD;
            }
            return dp[len][n][m];
       
    }
};

滚动数组优化维度:

class Solution {
public:
    int profitableSchemes(int n, int m, vector<int>& g, vector<int>& p) {
         //01背包问题 一个工作可以选择做或者不做profit  两个限制条件 一个是minProfit 一个是n
        //dp[i][j][k] 从前i个工作中选 人数要不超过j 利润至少为k 的所有选择计划。
        const int MOD=1e9+7;
        int len=g.size();
        vector<vector<int>> dp(n+1,vector<int>(m+1));
        //如果不做i工作 dp[i][j][k]=dp[i-1][j][k]
        //如果做了i工作 dp[i][j][k]+=dp[i-1][j-g[i-1]][k-p[i-1]]
        //分析初始化 如果i为0时 显然都为0 p为0时 有j=1
        for(int j=0;j<=n;++j) dp[j][0]=1; //完成初始化 只需考虑i为0的情况即可 因为其他都会特判
        //开始填表
        for(int i=1;i<=len;++i) //只需考虑i即可
          for(int j=n;j>=g[i-1];--j) 
            for(int k=m;k>=0;--k)
            {
                dp[j][k]+=dp[j-g[i-1]][max(k-p[i-1],0)];
                dp[j][k]%=MOD;
            }
            return dp[n][m];
    }
};

 三、组合总和IV(似包非包)

. - 力扣(LeetCode)

分析问题的过程中,发现重复子问题,然后抽象出一个状态表示

class Solution {
public:
//该题是排列总和 
//背包问题本质上解决的是  有限制条件的组合问题
    int combinationSum4(vector<int>& nums, int target) {
        vector<double> dp(target + 1);
        dp[0] = 1;
        for (int i = 1; i <= target; i++) 
            for (int& num : nums) 
                if (num <= i) dp[i] += dp[i - num];
        return dp[target];
    }
};

四、不同的二叉搜索树(卡特兰数)

. - 力扣(LeetCode)

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n+1);
        dp[0]=1;//dp[i]表示i个节点时有多少种搜索树
        for(int i=1;i<=n;++i)
          for(int j=1;j<=i;++j)
            dp[i]+=dp[j-1]*dp[i-j];
        return dp[n];
        //发现重复子问题,抽象出一种状态表示
    }
};

评论 123
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

✿༺小陈在拼命༻✿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值