879-盈利计划

题目

879. 盈利计划 - 力扣(LeetCode) (leetcode-cn.com)

思路

本题目核心问题为对于特定的minProfit和特定的n,在groups中选择一些工作使其满足要求nminProfit的限制,选择方案总共有多少种。因此,我们从最后一项工作group[last]开始考虑,对于该工作,有两个可能性:如果选择该工作,则问题转换为对于minProfit = minProfit - profit[last]以及n = n-group[last],在group中前last项中进行选择时满足要求方案数量;如果不选择该工作,则问题转换为对于minProfit以及n,在group中前last项中进行选择时满足要求方案数量。这两个子问题同原始问题的类型是完全一样的,将这两个子问题的结果求和即可得到原始问题的解,因此可以得到以下递归计算的公式。

f(i, n, minProfit) = f(i-1, n, minProfit) + f(i-1, n-group[i], minProfit - profit[i])

注意到在n >= group[i]的情况下我们才能选择执行第i项工作,并且对于minProfit <= 0的所有和minProfit=0的情况是等同的(即无需考虑minProfit的限制),因此上述公式种的minProfit - profit[i]应该更改为max(0, minProfit - profit[i])

注意对于minProfit <= 0的情况,至少有一种方案(即不执行任何工作),如果此时有工作可以执行,则还可以增加可能的方案数目。

根据上述递归计算公式,可以采用动态规划或者简单的递归来计算,下边的代码给出了动态规划的实现。由于f(i)仅同f(i-1)有关,因此三维动态规划可以降低为二维。

代码

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        /*高维vector通常较慢,直接使用静态数组则较快 */
        // vector<vector<int>> dp(n + 1, vector<int>(minProfit + 1));
        // 由于每次动态规划只依赖上一轮的结果,因此三维dp可以转化为二维
        int dp[101][101];
        int result = 0;
		
        /* 这里计算i=0的情况,i=0表示有第一项工作可以做而不是没有工作可以做 */
        for(int j=n; j >= 0; j--) {
            for(int k=minProfit; k >= 0; k--) {
                dp[j][k] = 0;
                /* 如果k为0,完全不做任何工作为一种方案 */
                if (k == 0) {
                    dp[j][k] += 1;
                }
                
                /* 如果当前工作可以做,并且做了之后获利大于等于k,则为一种方案 */
                if (j >= group[0] && k <= profit[0]) {
                    dp[j][k] += 1;
                }

            }
        }

        for(int i=1; i<group.size(); i++) {
            for(int j=n; j >= 0; j--) {
                for(int k=minProfit; k >= 0; k--) {
                    /* 不执行该工作时的方案数(同i-1时相同,因此无需任何操作) */
                    
                    /* 当前工作可以做,则增加选择执行该工作时可能的方案 */
                    if (j >= group[i]) {
                        int index = max(k-profit[i], 0);
                        dp[j][k] = (dp[j][k] + dp[j-group[i]][index]) % (1000000007);
                    }
                    
                }
            }
        }
        return dp[n][minProfit];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值