多属性背包进阶--盈利计划

Posted C_YCBX Py_YYDS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多属性背包进阶--盈利计划相关的知识,希望对你有一定的参考价值。

题目

在这里插入图片描述

题目解析

  • 题目描述第i种工作产生两个属性–产生的利润和需要的人数。
  • 而题目的含义在于:给你n个员工,问给他们分配工作后,产生至少是minProfit利润的计划总数(即不同的分配工作情况)。
    这与之前做过的一道多属性背包不可谓不相同:字符串装0和1

分析:

  • 此题明显和之前所做过的一道多属性背包类似,只不过这题的多属性较难看出,这题的属性也是两个,员工人数和所需的最小利润数,故根据这两个属性构建背包得dp关系 dp[i][j][k] = dp[i-1][j][k](no pack) + dp[i-1][j-group[i-1]][max(0,k-profit[i-1])],这里的dp[i][j][k] 的含义是在前i份工作的选择中,背包中有j名员工和要求至少是k的利润限制下的计划总数
  • 这与之前的多属性背包唯一的不同在于背包的第二个属性要求的是至少是k的利润而不是正好是k,所以我们通过max(0,k-profit[i-1])以保证大于或者等于k都能被计数base case dp[0][0][0] = 1;
  • 最后得到的答案需要的是所有盈利计划利润大于minProfit的情况,所以需要将员工[0-n]的情况都加上。
    由于题目要求结果要返回模10e9+7的结果,可以在求dp的时候得到每个dp[i][j][k]%MOD,然后最后再集体相加再模一次得结果。原理在于MOD运算的扩展:(a+b+c+d)%mod =(a%mod+b%mod+c%mod...)%mod.也可以(a+b+c)%mod = ((a+0)%mod+b)%mod+c)%mod

解题代码

三维数组实现

在这里插入图片描述

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        int MOD = 1e9 + 7;
        int size = group.size(); int dp[size+1][n+1][minProfit+1];
        memset(dp,0,sizeof(dp)); dp[0][0][0] = 1;
        //这一层表示枚举工作(物品)
        for(int i=1;i<=size;i++){
            //这一层表示枚举员工(背包容量/物品属性)
            for(int j=0;j<=n;j++){
                //这一层表示枚举最小利润(背包容量/物品属性)
                for(int k=0;k<=minProfit;k++){
                if(j>=group[i-1])
                    dp[i][j][k] = (dp[i-1][j][k]+dp[i-1][j-group[i-1]][max(0,k-profit[i-1])])%MOD;
                else
                    dp[i][j][k] = dp[i-1][j][k];
                }
            }
        }//由于题目要求的是给你n名员工,以及i份工作,要求必须达到minProfit的计划数,所以由于dp的定义,则可选择对象为i个以及minProfit是确定的,而员工n人随便如何分配,只要达到minProfit的计划都算。
        int res = 0;
        //如果不这样取模,最后再取,则int会溢出,这个方法叫做反复取模法,利用两个数的不断取模,得到最终答案。=>(a+b+c)%mod = ((a+0)%mod+b)%mod+c)%mod
        for(int i=0;i<=n;i++)res = (res + dp[size][i][minProfit])%MOD;

    return res;
    }
};

压缩一维得到二维形式

很明显可以压缩做外面一维。为了保证是上一行需要逆序遍历。

在这里插入图片描述

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        int MOD = 1e9 + 7;
        int size = group.size(); int dp[n+1][minProfit+1];
        memset(dp,0,sizeof(dp)); dp[0][0] = 1;
        //这一层表示枚举工作(物品)
        for(int i=1;i<=size;i++){
            //这一层表示枚举员工(背包容量/物品属性)
            for(int j=n;j>=0;j--){
                //这一层表示枚举最小利润(背包容量/物品属性)
                for(int k=minProfit;k>=0;k--){
                if(j>=group[i-1])
                    dp[j][k] = (dp[j][k]+dp[j-group[i-1]][max(0,k-profit[i-1])])%MOD;
                else
                    dp[j][k] = dp[j][k];
                }
            }
        }//由于题目要求的是给你n名员工,以及i份工作,要求必须达到minProfit的计划数,所以由于dp的定义,则可选择对象为i个以及minProfit是确定的,而员工n人随便如何分配,只要达到minProfit的计划都算。
        int res = 0;
        //如果不这样取模,最后再取,则int会溢出,这个方法叫做反复取模法,利用两个数的不断取模,得到最终答案。=>(a+b+c)%mod = ((a+0)%mod+b)%mod+c)%mod
        for(int i=0;i<=n;i++)res = (res + dp[i][minProfit])%MOD;

    return res;
    }
};

以上是关于多属性背包进阶--盈利计划的主要内容,如果未能解决你的问题,请参考以下文章

879. 盈利计划(多维背包问题)

879. 盈利计划(多维背包问题)

LeetCode 879. 盈利计划

背包DP(数位成本和为目标值的最大数字+完全平方数+零钱兑换+盈利计划+最后一块石头的重量+目标和+1和0)

每日一题--多属性的01背包问题

蒟蒻吃药计划-治疗系列 #round4 多重背包+混合背包代码存放