https://leetcode.com/problems/profitable-schemes/
https://leetcode.com/articles/profitable-schemes/
https://leetcode.com/problems/profitable-schemes/discuss/157099/Java-original-3d-to-2d-DP-solution
https://leetcode.com/problems/profitable-schemes/discuss/154617/C%2B%2BJavaPython-DP
https://zxi.mytechroad.com/blog/dynamic-programming/leetcode-879-profitable-schemes/
X. Video
č±č±é ± LeetCode 879. Profitable Schemes - å·é¢ę¾å·„ä½ EP212
There are G people in a gang, and a list of various crimes they could commit.
The
i
-th crime generates a profit[i]
and requires group[i]
gang members to participate.
If a gang member participates in one crime, that member can't participate in another crime.
Let's call a profitable scheme any subset of these crimes that generates at least
P
profit, and the total number of gang members participating in that subset of crimes is at most G.
How many schemes can be chosen? Since the answer may be very large, return it modulo
10^9 + 7
.
Example 1:
Input: G = 5, P = 3, group = [2,2], profit = [2,3] Output: 2 Explanation: To make a profit of at least 3, the gang could either commit crimes 0 and 1, or just crime 1. In total, there are 2 schemes.
Example 2:
Input: G = 10, P = 5, group = [2,3,5], profit = [6,7,8] Output: 7 Explanation: To make a profit of at least 5, the gang could commit any crimes, as long as they commit one. There are 7 possible schemes: (0), (1), (2), (0,1), (0,2), (1,2), and (0,1,2).
Note:
1 <= G <= 100
0 <= P <= 100
1 <= group[i] <= 100
0 <= profit[i] <= 100
1 <= group.length = profit.length <= 100
https://leetcode.com/articles/profitable-schemes/
- Time Complexity: , where is the number of crimes available to the gang.
- Space Complexity: .
We don't care about the profit of the scheme if it is , because it surely will be over the threshold of profitability required. Similarly, we don't care about the number of people required in the scheme if it is , since we know the scheme will be too big for the gang to execute.
As a result, the bounds are small enough to use dynamic programming. Let's keep track of
cur[p][g]
, the number of schemes with profitability and requiring gang members: except we'll say (without changing the answer) that all schemes that profit at least P
dollars will instead profit exactly P
dollars.
Algorithm
Keeping track of
cur[p][g]
as defined above, let's understand how it changes as we consider 1 extra crime that will profit p0
and require g0
gang members. We will put the updated counts into cur2
.
For each possible scheme with profit
p1
and group size g1
, that scheme plus the extra crime (p0, g0
) being considered, has a profit of p2 = min(p1 + p0, P)
, and uses a group size of g2 = g1 + g0
.public int profitableSchemes(int G, int P, int[] group, int[] profit) { int MOD = 1_000_000_007; int N = group.length; long[][][] dp = new long[2][P+1][G+1]; dp[0][0][0] = 1; for (int i = 0; i < N; ++i) { int p0 = profit[i]; // the current crime profit int g0 = group[i]; // the current crime group size long[][] cur = dp[i % 2]; long[][] cur2 = dp[(i + 1) % 2]; // Deep copy cur into cur2 for (int jp = 0; jp <= P; ++jp) for (int jg = 0; jg <= G; ++jg) cur2[jp][jg] = cur[jp][jg]; for (int p1 = 0; p1 <= P; ++p1) { // p1 : the current profit // p2 : the new profit after committing this crime int p2 = Math.min(p1 + p0, P); for (int g1 = 0; g1 <= G - g0; ++g1) { // g1 : the current group size // g2 : the new group size after committing this crime int g2 = g1 + g0; cur2[p2][g2] += cur[p1][g1]; cur2[p2][g2] %= MOD; } } } // Sum all schemes with profit P and group size 0 <= g <= G. long ans = 0; for (long x: dp[N%2][P]) ans += x; return (int) ans; }
https://leetcode.com/problems/profitable-schemes/discuss/154617/C%2B%2BJavaPython-DP
Well, it is a Knapsack problem and my first intuition is dp.
dp[i][j]
means the count of schemes with i
profit and j
members.
The dp equation is simple here:
dp[i + p][j + g] += dp[i][j])
if i + p < P
dp[P][j + g] += dp[i][j])
if i + p >= P
Don't forget to take care of overflow.
For each pair
(p, g)
of (profit, group)
, I update the count in dp
.
Time Complexity:
O(NPG)
O(NPG)
public int profitableSchemes(int G, int P, int[] group, int[] profit) {
int[][] dp = new int[P + 1][G + 1];
dp[0][0] = 1;
int res = 0, mod = (int)1e9 + 7;
for (int k = 0; k < group.length; k++) {
int g = group[k], p = profit[k];
for (int i = P; i >= 0; i--)
for (int j = G - g; j >= 0; j--)
dp[Math.min(i + p, P)][j + g] = (dp[Math.min(i + p, P)][j + g] + dp[i][j]) % mod;
}
for (int x : dp[P]) res = (res + x) % mod;
return res;
}
We don't care about the profit of the scheme if it is , because it surely will be over the threshold of profitability required. Similarly, we don't care about the number of people required in the scheme if it is , since we know the scheme will be too big for the gang to execute.
As a result, the bounds are small enough to use dynamic programming. Let's keep track of
cur[p][g]
, the number of schemes with profitability and requiring gang members: except we'll say (without changing the answer) that all schemes that profit at least P
dollars will instead profit exactly P
dollars.
Algorithm
Keeping track of
cur[p][g]
as defined above, let's understand how it changes as we consider 1 extra crime that will profit p0
and require g0
gang members. We will put the updated counts into cur2
.
For each possible scheme with profit
p1
and group size g1
, that scheme plus the extra crime (p0, g0
) being considered, has a profit of p2 = min(p1 + p0, P)
, and uses a group size of g2 = g1 + g0
.
public int profitableSchemes(int G, int P, int[] group, int[] profit) {
int MOD = 1_000_000_007;
int N = group.length;
long[][][] dp = new long[2][P + 1][G + 1];
dp[0][0][0] = 1;
for (int i = 0; i < N; ++i) {
int p0 = profit[i]; // the current crime profit
int g0 = group[i]; // the current crime group size
long[][] cur = dp[i % 2];
long[][] cur2 = dp[(i + 1) % 2];
// Deep copy cur into cur2
for (int jp = 0; jp <= P; ++jp)
for (int jg = 0; jg <= G; ++jg)
cur2[jp][jg] = cur[jp][jg];
for (int p1 = 0; p1 <= P; ++p1) { // p1 : the current profit
// p2 : the new profit after committing this crime
int p2 = Math.min(p1 + p0, P);
for (int g1 = 0; g1 <= G - g0; ++g1) { // g1 : the current group size
// g2 : the new group size after committing this crime
int g2 = g1 + g0;
cur2[p2][g2] += cur[p1][g1];
cur2[p2][g2] %= MOD;
}
}
}
// Sum all schemes with profit P and group size 0 <= g <= G.
long ans = 0;
for (long x : dp[N % 2][P])
ans += x;
return (int) ans;
}
https://zxi.mytechroad.com/blog/dynamic-programming/leetcode-879-profitable-schemes/
v1: Dimension reduction by copying.
int profitableSchemes(int G, int P, vector<int>& group, vector<int>& profit) { const int kMod = 1000000007; const int K = group.size(); // dp[i][j]:= # of schemes of making profit i with j people. vector<vector<int>> dp(P + 1, vector<int>(G + 1, 0)); dp[0][0] = 1; for (int k = 1; k <= K; ++k) { auto tmp = dp; const int p = profit[k - 1]; const int g = group[k - 1]; for (int i = 0; i <= P; ++i) for (int j = 0; j <= G; ++j) tmp[i][j] = (tmp[i][j] + (j < g ? 0 : dp[max(0, i - p)][j - g])) % kMod; dp.swap(tmp); } return accumulate(begin(dp[P]), end(dp[P]), 0LL) % kMod; }
v2: Dimension reduction by using rolling array.
int profitableSchemes(int G, int P, vector<int>& group, vector<int>& profit) { const int kMod = 1000000007; // dp[i][j]:= # of schemes of making profit i with j people. vector<vector<int>> dp(P + 1, vector<int>(G + 1, 0)); dp[0][0] = 1; const int K = group.size(); for (int k = 0; k < K; ++k) { const int p = profit[k]; const int g = group[k]; for (int i = P; i >= 0; --i) { const int ip = min(P, i + p); for (int j = G - g; j >= 0; --j) dp[ip][j + g] = (dp[ip][j + g] + dp[i][j]) % kMod; } } return accumulate(begin(dp[P]), end(dp[P]), 0LL) % kMod; }https://www.acwing.com/solution/LeetCode/content/581/
åø®ę“¾éę
G
åęå,ä»ä»¬åÆč½ēÆäøåē§åę ·ēē½Ŗč”。
ē¬¬
i
ē§ēÆē½Ŗä¼äŗ§ē profit[i]
ēå©ę¶¦,å®č¦ę± group[i]
åęåå
±ååäø。
让ę们ęčæäŗēÆē½Ŗēä»»ä½åéē§°äøŗēå©č®”å,čÆ„č®”åč³å°äŗ§ē
P
ēå©ę¶¦。
ęå¤å°ē§ę¹ę”åÆ仄éę©?å äøŗēę”å¾å¤§,ę仄čæåå®ęØ”
10^9 + 7
ēå¼。ę ·ä¾
č¾å
„:G = 5, P = 3, group = [2,2], profit = [2,3]
č¾åŗ:2
č§£é:
č³å°äŗ§ē 3 ēå©ę¶¦,čÆ„åø®ę“¾åÆ仄ēÆäøē½Ŗ 0 åē½Ŗ 1,ęä»
ēÆäøē½Ŗ 1。
ę»ēę„čÆ“,ęäø¤ē§ę¹ę”。
č¾å
„:G = 10, P = 5, group = [2,3,5], profit = [6,7,8]
č¾åŗ:7
č§£é:
č³å°äŗ§ē 5 ēå©ę¶¦,åŖč¦ä»ä»¬ēÆå
¶äøäøē§ē½Ŗå°±č”,ę仄čÆ„åø®ę“¾åÆ仄ēÆäøä»»ä½ē½Ŗč”。
ę 7 ē§åÆč½ēč®”å:(0),(1),(2),(0,1),(0,2),(1,2),仄å (0,1,2)。
- é¢ē®č¦ę±ęÆē»č®”å©ę¶¦č³å°äøŗ
P
,äøäŗŗę°ęå¤äøŗ G
ēę¹ę”ę°。ē±äŗå©ę¶¦ęå¤ęåÆč½č¾¾å° 100 * n
,ę°ę®čå“čæ大čäøę¹ä¾æčæč”åØęč§å,åÆ仄čččÆ„é®é¢ēåƹå¶é®é¢。å³ē»č®”äŗŗę°ęå¤äøŗ G
ēę¹ę”ę°,åå»å©ę¶¦å°äŗ P
,äøē»č®”äŗŗę°ęå¤äøŗ G
ēę¹ę”ę°。
- åƹäŗē¬¬äøéØå,åØęč§åēē¶ęäøŗ ,č”Øē¤ŗččäŗå i äøŖč®”å,åäøäŗŗę°äøŗ j ēę¹ę”ę°ęÆå¤å°。åƹäŗē¬¬ i äøŖč®”å,。åå§å¼ 。äŗŗę°ęå¤äøŗ
G
ēę¹ę”ę°äøŗ 。å®é
äøåÆ仄ēē„ęē¬¬äøē»“。
- åƹäŗē¬¬äŗéØå,åØęč§åēē¶ęäøŗ ,č”Øē¤ŗččäŗå i äøŖč®”å,åäøäŗŗę°äøŗ j ēę¹ę”ę°,äøå©ę¶¦äøŗ k ēę¹ę”ę°ęÆå¤å°。åƹäŗē¬¬ i äøŖč®”å,。åå§å¼ 。å©ę¶¦å°äŗ
P
,äøē»č®”äŗŗę°ęå¤äøŗ G
ēę¹ę”ę°äøŗ 。å®é
äøåÆ仄ä»ē¶ēē„ęē¬¬äøē»“。
- ęē»ēę”å°±ęÆäø¤éØååå·®。
X. Video
č±č±é ± LeetCode 879. Profitable Schemes - å·é¢ę¾å·„ä½ EP212