前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DP:二维费用背包问题+似包非包

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

作者头像
小陈在拼命
发布2024-06-28 09:17:19
490
发布2024-06-28 09:17:19
举报
二维费用的背包问题:大多以01背包为基础,存在两个限制条件!

一、一和零

. - 力扣(LeetCode)

代码语言:javascript
复制
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];
    }
};

滚动数组优化一个维度

代码语言:javascript
复制
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)

代码语言:javascript
复制
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];
       
    }
};

滚动数组优化维度:

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
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)

代码语言:javascript
复制
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];
        //发现重复子问题,抽象出一种状态表示
    }
};
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、一和零
  • 二、盈利计划(非常经典)
  • 三、组合总和IV(似包非包)
  • 四、不同的二叉搜索树(卡特兰数)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档