leetcode 879.盈利计划

1.任务 task

        (1)题目描述:集团里有 n 名员工,他们可以完成各种各样的工作创造利润。第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作,就不能参与另一项工作。工作的任何至少产生 minProfit 利润的子集称为 盈利计划 。并且工作的成员总数最多为 n 。有多少种计划可以选择?因为答案很大,所以 返回结果模 10^9 + 7 的值。

        示例 1:

        输入:n = 5, minProfit = 3, group = [2,2], profit = [2,3]

        输出:2

        解释:至少产生 3 的利润,该集团可以完成工作 0 和工作 1 ,或仅完成工作 1 。总的来说,有两种计划。

        示例 2:

        输入:n = 10, minProfit = 5, group = [2,3,5], profit = [6,7,8]

        输出:7

        解释:至少产生 5 的利润,只要完成其中一种工作就行,所以该集团可以完成任何工作。有 7 种可能的计划:(0),(1),(2),(0,1),(0,2),(1,2),以及 (0,1,2)。

2.任务分解

        传统传统的0-1背包问题采用的方法有很多种,此题采用动态规划来完成。本题从直观上看并不是简单的0-1背包问题,而是多重背包问题。此种问题的解法类似于简单的0-1背包问题,关键在于状态转移方程的查找,将问题转化并分解为寻找状态转移方程、dp数组设计及初始化。

        分层此处简要分为两层,第一层为I/O,确定输入输出的变量和结构,第二层是状态转换方程的具体实现。

3.关键变量

        输入:

        n:n名worker;

        minProfit:指定达到的最小利润;

        group[ ],profit[ ]:分别对应于特定工作的人数和能产生的利润。

        输出:

        dp[n][minProfit]:对应于最多使用n名worker、至少产生minProfit的方案数。    

4.算法步骤

        (1)此处是多重背包问题,可采用三维数组来解决,定义数组dp[i][j][k],含义为前i个工作,使用人数为j个,利润至少为k的方案数,为优化算法执行的速度,我们知道dp[i][j][k]中的i只与[i-1]有关,因此我们可以采用二维数组dp[j][k]来解决问题,不过此时的含义表示最多使用j个人至少产生k利润的方案数,去掉i后应倒序推导,否则会出现值被覆盖的情况;

        (2)确定dp数组初始化的值,二维数组dp[j][k],当k = 0时,无论人数,其为1种方案,可初始化dp数组dp[j][0] = 1;

        (3)确定状态转换方程。将每种工作看作一个物品,对于每件物品,和0-1背包问题类似,我们可选可不选,选为1,不选为0。


dp[j][k] = \left\{\begin{matrix} dp[j][k]&,0\\dp[j-group[i-1]][max(k-profit[i-1],0)] & ,1\end{matrix}\right.

        利润k-profit[i-1]出现负值时,表示没有利润,令为0即可,此处的dp[j][k]应为两种情况的和。

        (4)返回最多使用n名工人,至少利润为minProfit的方案数dp[n][minProfit]。

5.算法流程图

        

6.code

//java
class Solution {
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        int mod = (int)1e9 + 7;//按题目要求取模
        int [][]dp = new int[n+1][minProfit+1]; //降维处理
        for(int i = 0; i <= n; i++)  //dp数组初始化,利润为0,人数无要求,方案数为1
            dp[i][0] = 1;
        for(int i = 1; i <= group.length; i++){    // 状态转换方程递推
            for(int j = n; j >= group[i-1]; j--){
                for(int k = minProfit; k >= 0; k--){
                    int s = Math.max(0, (k - profit[i-1])); //为负表示什么都不做,设为0
                    dp[j][k] = (dp[j][k] + dp[j-group[i-1]][s]) % mod;
                }
            }
        }
        return dp[n][minProfit];  
    }
}

7.总结

        0-1背包问题的变种很多,如完全背包问题、多重背包问题等,若采用DP来解决,关键问题在于dp数组的设计、初始化以及状态转移方程的推导,dp数组的设计在一定程度上能够影响算法的执行速度。

        如有理解错误请批评指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值