阿犇

记录生活中的点点滴滴

0%

盈利计划

Trace

6.9 每日一题

传送门:https://leetcode-cn.com/problems/profitable-schemes/

Problem

集团里有 n 名员工,他们可以完成各种各样的工作创造利润。

i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作,就不能参与另一项工作。

工作的任何至少产生 minProfit 利润的子集称为 盈利计划 。并且工作的成员总数最多为 n

有多少种计划可以选择?因为答案很大,所以 返回结果模 10^9 + 7 的值

示例

1
2
3
4
5
6
7
8
9
输入:n = 5, minProfit = 3, group = [2,2], profit = [2,3]
输出:2
解释:至少产生 3 的利润,该集团可以完成工作 0 和工作 1 ,或仅完成工作 1 。
总的来说,有两种计划。

输入: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) 。

数据范围

1
2
3
4
5
6
1 <= n <= 100
0 <= minProfit <= 100
1 <= group.length <= 100
1 <= group[i] <= 100
profit.length == group.length
0 <= profit[i] <= 100

思路

三维$dp$,考虑为什么用三维,首先考虑前$i$个员工,成员数限制(类似背包问题的体积),利润(类似背包问题价值),属性是方法种数,很自然就会想到三维。

f[i][j][k]: 考虑前i种工作,代价恰好为j,获得至少利润k的方法种数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
int sum=0;
int count=0;
for(int i=0;i<profit.size();i++){
sum+=profit[i];
}
const int mod = 1e9+7;
// 需要考虑三维dp
// f[i][j][k]: 考虑前i种工作,代价恰好为j,获得至少利润k的方法种数。
// 时间复杂度:100*100*100=1e6
int f[101][101][101];
f[0][0][0]=1;
for(int i=1;i<=group.size();i++){
for(int j=0;j<=n;j++){
for(int k=0;k<=minProfit;k++){
f[i][j][k]=f[i-1][j][k];
if(j>=group[i-1]){
f[i][j][k]=(f[i][j][k]+f[i-1][j-group[i-1]][max(0,k-profit[i-1])])%mod;
}
}

}
}
for(int i=0;i<=n;i++){
count=(count+f[group.size()][i][minProfit])%mod;
}
return count;
}
};

f[i][j][k]=(f[i][j][k]+f[i-1][j-group[i-1]][max(0,k-profit[i-1])])%mod;当中max(0,k-profit[i-1])其实相当于

if((k-profit[i-1])>=0)f[i][j][k]=(f[i][j][k]+f[i-1][j-group[i-1]][k-profit[i-1]])%mod;

elsef[i][j][k]=(f[i][j][k]+f[i-1][j-group[i-1]][0])%mod;

另一种写法:

f[i][j][k]: 考虑前i种工作,代价至多为j,获得至少利润k的方法种数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
int sum=0;
int count=0;
for(int i=0;i<profit.size();i++){
sum+=profit[i];
}
const int mod = 1e9+7;
// 需要考虑三维dp
// f[i][j][k]: 考虑前i种工作,代价至多为j,获得至少利润k的方法种数。
// 时间复杂度:100*100*100=1e6
int f[101][101][101];
// f[0][0][0]=1;
for(int i=0;i<=group.size();i++){
for(int j=0;j<=n;j++){
f[i][j][0]=1;
}
}

for(int i=1;i<=group.size();i++){
for(int j=0;j<=n;j++){
for(int k=0;k<=minProfit;k++){
f[i][j][k]=f[i-1][j][k];
if(j>=group[i-1]){
f[i][j][k]=(f[i][j][k]+f[i-1][j-group[i-1]][max(0,k-profit[i-1])])%mod;
}
}

}
}
return f[group.size()][n][minProfit]%mod;
}
};

$dp$数组要根据具体意义来初始化。

还可以优化到二维,类似01背包。

您的支持是我继续创作的最大动力!