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

题目

在这里插入图片描述

题目解析

  • 题目描述第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的利润限制下的计划总数。 本来如果要求只要等于 minProfit 的话,则只有 profit[i-1] 小于或等于 k 状态才可转移(该物品才可取),本题的情况,则将大于等于 k 的物品属性直接等价于等于 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];
                }
            }
        }
        int res = 0;
        for(int i=0;i<=n;i++)res = (res + dp[i][minProfit])%MOD;

    return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值