879. Profitable Schemes
- https://leetcode.com/problems/profitable-schemes/description/
- 给人数,任务需要的人数,最小利益,求满足最小利益的方案数
思路
- 动态规划,有三个限制条件,用这三个建立一个三位数组作为 dp
- 本题解法的三个维度分别为:当前可选择的工作 i,已选择的小组员工人数 j,以及目前状态的工作获利下限 k。
- 状态转移方程
- 不能开展工作:dp[i][j][k]=dp[i−1][j][k]
- 能开展:dp[i][j][k]=dp[i−1][j][k]+dp[i−1]j−group[i]][max(0,k−profit[i])]
代码
class Solution {
public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
int len = profit.length;
int[][][] dp = new int[len + 1][n + 1][minProfit + 1];
int mod = (int)1e9+7;
dp[0][0][0] = 1;
for(int i = 1; i <= len; i++){
int need = group[i - 1];
int get = profit[i - 1];
for(int j = 0; j <= n; j++){
for(int k = 0; k <= minProfit; k++){
if(j < need){
// no enough people, can't do the work
// thus, the dp = last task's dp
dp[i][j][k] = dp[i - 1][j][k];
}else{
dp[i][j][k] = (dp[i - 1][j][k] + dp[i - 1][j - need][Math.max(0, k-get)]) % mod;
}
}
}
}
// for(int i = 0; i < len; i++){
// for(int j = 0; j < n; j++){
// System.out.print(Arrays.toString(dp[i][j]));
// }
// System.out.print("\n");
// }
int res = 0;
for(int i = 0; i <= n; i++){
res = (res + dp[len][i][minProfit]) % mod;
}
return res;
}
}
递归方案
- 对于每个任务,有做或者不做两种可能,这样就是一个二叉树。
class Solution {
int mod = 1000000007;
int[][][] memo = new int[101][101][101];
int find(int pos, int count, int profit, int n, int minProfit, int[] group, int[] profits) {
if (pos == group.length) {
// If profit exceeds the minimum required; it's a profitable scheme.
return profit >= minProfit ? 1 : 0;
}
if (memo[pos][count][profit] != -1) {
// Repeated subproblem, return the stored answer.
return memo[pos][count][profit];
}
// Ways to get a profitable scheme without this crime.
int totalWays = find(pos + 1, count, profit, n, minProfit, group, profits);
if (count + group[pos] <= n) {
// Adding ways to get profitable schemes, including this crime.
totalWays += find(pos + 1, count + group[pos], Math.min(minProfit, profit + profits[pos]), n, minProfit, group, profits);
}
return memo[pos][count][profit] = totalWays % mod;
}
public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
// Initializing all states as -1.
for (int i = 0; i <= group.length; i++) {
for(int j = 0; j <= n; j++) {
Arrays.fill(memo[i][j], -1);
}
}
return find(0, 0, 0, n, minProfit, group, profit);
}
}
参考
- https://leetcode.cn/problems/profitable-schemes/solutions/819654/ying-li-ji-hua-by-leetcode-solution-3t8o/
public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
// len 工作的数量
int len = group.length, MOD = (int)1e9 + 7;
// dp[i][j][k]:前 i 个工作中选择了 j 个员工,并且满足工作利润至少为 k 的情况下盈利计划的总数目
int[][][] dp = new int[len + 1][n + 1][minProfit + 1];
// 初始化,员工为 0,选择了 0 个员工,工作利润为 0
// 即什么都不做,方案数为 1
dp[0][0][0] = 1;
for (int i = 1; i <= len; i++) {
// 第 i 份工作需要使用的人数
int members = group[i - 1];
// 第 i 份工作能获取的利润
int earn = profit[i - 1];
for (int j = 0; j <= n; j++) {
for (int k = 0; k <= minProfit; k++) {
if (j < members) {
// 当前能提供的人工数量小于工作需要的人数,无法做该工作
dp[i][j][k] = dp[i - 1][j][k];
} else {
// 当人能提供的人工数量满足工作需要的人数,可以做该工作
// 为什么是 Math.max(0, k - profit[i - 1]),k 表示当前需要的最小利润
// if (k >= profit[i - 1]),前 i-1 种工作中选择的利润至少要达到 k-profit[i-1]
// if (k < profit[i -1]),说明前 i - 1 中工作的利润只要 >= 0 即可
dp[i][j][k] = (dp[i - 1][j][k] + dp[i - 1][j - members][Math.max(0, k - earn)]) % MOD;
}
}
}
}
int sum = 0;
for (int j = 0; j <= n; j++) {
sum = (sum + dp[len][j][minProfit]) % MOD;
}
return sum;
}