https://leetcode-cn.com/problems/profitable-schemes/
思路:依旧和01背包问题很像,考虑用
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k表示前
i
i
i种工作使用了
j
j
j名员工且利润为
k
k
k的方案数,在不考虑取模的情况下,如果当前的
j
、
k
j、k
j、k不满足第
i
i
i种工作的限制,那么有:
d
p
i
,
j
,
k
=
d
p
i
−
1
,
j
,
k
dp_{i,j,k}=dp_{i-1,j,k}
dpi,j,k=dpi−1,j,k
否则有:
d
p
i
,
j
,
k
=
d
p
i
−
1
,
j
,
k
+
d
p
i
−
1
,
j
−
g
r
o
u
p
i
,
k
−
p
r
o
f
i
t
i
dp_{i,j,k}=dp_{i-1,j,k}+dp_{i-1,j-group_i,k-profit_i}
dpi,j,k=dpi−1,j,k+dpi−1,j−groupi,k−profiti
但是这样dp的话第三维的取值范围为
[
0
,
100
∗
100
]
[0,100*100]
[0,100∗100],再加上前两维大约有
1
0
8
10^8
108,会超时。考虑对
d
p
dp
dp定义做一点修改,用
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k表示前
i
i
i种工作使用了
j
j
j名员工且利润至少为
k
k
k的方案数。此时转移方程变为:
d
p
i
,
j
,
k
=
d
p
i
−
1
,
j
,
k
+
d
p
i
−
1
,
j
−
g
r
o
u
p
i
,
m
a
x
(
0
,
k
−
p
r
o
f
i
t
i
)
dp_{i,j,k}=dp_{i-1,j,k}+dp_{i-1,j-group_i,max(0,k-profit_i)}
dpi,j,k=dpi−1,j,k+dpi−1,j−groupi,max(0,k−profiti)
这样第三维就降低到了
[
0
,
100
]
[0,100]
[0,100],可以通过本题。类比使用01背包的空间优化策略,我们可以省去第一维。初始化
d
p
[
0
]
[
0
]
=
1
dp[0][0]=1
dp[0][0]=1。
class Solution {
public:
int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
vector<vector<int>> dp(n+1,vector<int>(minProfit+1));
dp[0][0]=1;
int len=group.size();
const int mod=1e9+7;
for(int i=0;i<len;i++)
{
for(int j=n;j>=group[i];j--)
for(int k=minProfit;k>=0;k--)
dp[j][k]=(dp[j][k]+dp[j-group[i]][max(0,k-profit[i])])%mod;
}
int ans=0;
for(int i=0;i<=n;i++)
ans=(ans+dp[i][minProfit])%mod;
return ans;
}
};
更进一步,我们可以定义 d p i , j , k dp_{i,j,k} dpi,j,k表示前 i i i种工作至多使用了 j j j名员工且利润为 k k k的方案数。转移方程没有变化,但是初始化和返回值要更改:
class Solution {
public:
int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
vector<vector<int>> dp(n+1,vector<int>(minProfit+1));
for(int i=0;i<=n;i++)
dp[i][0]=1;
int len=group.size();
const int mod=1e9+7;
for(int i=0;i<len;i++)
{
for(int j=n;j>=group[i];j--)
for(int k=minProfit;k>=0;k--)
dp[j][k]=(dp[j][k]+dp[j-group[i]][max(0,k-profit[i])])%mod;
}
return dp[n][minProfit];
}
};