Leetcode 879. Profitable Schemes(Hard)

879. Profitable Schemes(Hard)

##Dynamic Programming##

本题是01背包问题

对于每个工作,都可以选择做或者不做,在这里,每份工作相当于每个物品,都可以取或者不取,而员工数量相当于体积限制,而且还需要满足额外条件最小利益

DP1

f[i][j][k]表示考虑前i项工作,只有j名员工,要达到利益为k的方案数

对于第i项工作,可以选择选或不选

  • 选,则f[i][j][k] = f[i - 1][j - group[i]][k -profit[i]],对于之前的i - 1项工作,只要用j - group[i]的员工,达到k - profit[i]的利益即可满足结果
    • 需要保证j - group[i] > 0
    • k - profit[i] < 0即表示仅仅完成第i项工作都能满足利益k的要求,此时应该改为f[i - 1][j - group[i]][0]
  • 不选,则f[i][j][k] = f[i - 1][j][k],表示只要之前的i - 1项工作,只要用j名员工,达到k的利益即可满足结果

状态转移方程: f ( i , j , k ) = f ( i − 1 , j − g r o u p ( i ) , m a x { k − p r o f i t ( i ) , 0 } ) + f ( i − 1 , j , k ) f(i,j,k) = f(i - 1,j - group(i),max\{k - profit(i), 0\})+f(i-1,j,k) f(i,j,k)=f(i1,jgroup(i),max{kprofit(i),0})+f(i1,j,k)

初始化:f[i][j][0]表示在处理第1 ~ i项工作时,可用员工为j,能获得的利润至少为0的方案数,应该理解为,一开始进入这个状态,拥有0元,方案数应该初始化为1

时间复杂度: O ( N × P × G ) O(N\times P\times G) O(N×P×G)
N为员工数量,P为允许的最小利润,G为工作的数量

class Solution {
    int MOD = 1_000_000_007;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        int[][][] dp = new int[110][110][110];
        int m = group.length;

        for (int i = 0; i <= m; i ++) {
            for (int j = 0; j <= n; j ++) {
                dp[i][j][0] = 1;
            }
        }

        for (int i = 1; i <= m; i ++) {
            for (int j = 1; j <= n; j ++) {
                for (int k = 0; k <= minProfit ; k ++) {
                    dp[i][j][k] = dp[i - 1][j][k];
                    if (j - group[i - 1] >= 0) {
                        int nk = Math.max(0, k - profit[i - 1]);
                        dp[i][j][k] += dp[i - 1][j - group[i - 1]][nk];
                    }
                    dp[i][j][k] %= MOD;
                }
            }
        }

        return dp[m][n][minProfit];
        
    }
}

应为dp[i][j][k]在迭代时至于dp[i - 1]有关,因此可以对空间进行压缩,注意01背包体积从大到小遍历

class Solution {
    int MOD = 1_000_000_007;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        int m = group.length;
        int[][] dp = new int[n + 1][minProfit + 1];

        for (int j = 0; j <= n; j ++) {
            dp[j][0] = 1;
        }

        for (int i = 1; i <= m; i ++) {
            for (int j = n; j >= 1; j --) {
                for (int k = minProfit; k >= 0 ; k --) {
                    dp[j][k] = dp[j][k];
                    if (j - group[i - 1] >= 0) {
                        int nk = Math.max(0, k - profit[i - 1]);
                        dp[j][k] += dp[j - group[i - 1]][nk];
                    }
                    dp[j][k] %= MOD;
                }
            }
        }

        return dp[n][minProfit];

    }
}

DP2

f[k][i][j]表示为,在遍历到1~k项工作时满足利益为i,员工数量为j的方案数,设第k项工作的利润为p,所需员工数量为g

f[k][i + p][j + g] = f[k - 1][i + p][j + g] + f[k - 1][i][j],当i + p > minProfit时,所获得利润i + p已经大于题目满足的minProfit,设P = minProfit
因此f[k][min(i + p, P)][j + g] = f[k - 1][min(i + p, P)][j + g] + f[k - 1][i][j]

由于f[k]仅仅与f[k - 1]有关,因此可以压缩空间,三维数组f[k][i][j]转成二维数组f[i][j]

初始化:f[0][0]表示员工为0时满足利益为0的方案数,应该为1

class Solution {
    int MOD = 1_000_000_007;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        int m = group.length;
        int[][] dp = new int[minProfit + 1][n + 1];

        dp[0][0] = 1;
        for (int k = 0; k < m; k ++) {
            int g = group[k], p = profit[k];
            for (int i = minProfit; i >= 0; i --) {
                for (int j = n - g; j >= 0; j --) {
                    dp[Math.min(i + p, minProfit)][j + g] = (dp[Math.min(i + p, minProfit)][j + g] + dp[i][j]) % MOD;
                }
            }
        }

        int res = 0;
        for (int i : dp[minProfit]) res = (res + i) % MOD;
        return res;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值