LeetCode 879 - Profitable Schemes


https://leetcode.com/problems/profitable-schemes/
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. 1 <= G <= 100
  2. 0 <= P <= 100
  3. 1 <= group[i] <= 100
  4. 0 <= profit[i] <= 100
  5. 1 <= group.length = profit.length <= 100

https://leetcode.com/articles/profitable-schemes/
  • Time Complexity: O(N * P * G), where N is the number of crimes available to the gang.
  • Space Complexity: O(P * G)
We don't care about the profit of the scheme if it is \geq P, 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 > G, 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 p and requiring g 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/157099/Java-original-3d-to-2d-DP-solution
dp[k][i][j] means for first k crime with i members and at least j profit, what is total schemes can be chosen.
And we need this Math.max(0, j - p), because this is for at least j profit.
dp[k][i][j] = dp[k - 1][i][j] + dp[k - 1][i - current group][Math.max(0, j - current profit)]
This is 3d original solution:
class Solution {
    private int mod = (int)1e9 + 7;
    public int profitableSchemes(int G, int P, int[] group, int[] profit) {
        int[][][] dp = new int[group.length + 1][G + 1][P + 1];
        dp[0][0][0] = 1;
        for (int k = 1; k <= group.length; k++) {
            int g = group[k - 1];
            int p = profit[k - 1];
            for (int i = 0; i <= G; i++) {
                for (int j = 0; j <= P; j++) {
                    dp[k][i][j] = dp[k - 1][i][j];
                    if (i >= g) {
                        dp[k][i][j] = (dp[k][i][j] + dp[k - 1][i - g][Math.max(0, j - p)])%mod;
                    }
                }
            }
        }
        int sum = 0;                                                       
        for(int i = 0; i <= G; i++){
            sum = (sum + dp[group.length][i][P])%mod;
        }
        return sum;
    }
}
Because all k dimension only depends on k - 1 dimension, so we can improve it to 2D array which only has i and j dimension.
Just be careful about that i and j should be decrease, in order to get correct old k - 1 dimension value.
class Solution {
    private int mod = (int)1e9 + 7;
    public int profitableSchemes(int G, int P, int[] group, int[] profit) {
        int[][] dp = new int[G + 1][P + 1];
        dp[0][0] = 1;
        for (int k = 1; k <= group.length; k++) {
            int g = group[k - 1];
            int p = profit[k - 1];
            for (int i = G; i >= g; i--) {
                for (int j = P; j >= 0; j--) {
                    dp[i][j] = (dp[i][j] + dp[i - g][Math.max(0, j - p)])%mod;
                }
            }
        }
        int sum = 0;                                                       
        for(int i = 0; i <= G; i++){
            sum = (sum + dp[i][P])%mod;
        }
        return sum;
    }
}

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)

    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 \geq P, 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 > G, 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 p and requiring g 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)。

  1. 题ē›®č¦ę±‚ę˜Æē»Ÿč®”åˆ©ę¶¦č‡³å°‘äøŗ P,äø”äŗŗę•°ęœ€å¤šäøŗ G ēš„ę–¹ę”ˆę•°。ē”±äŗŽåˆ©ę¶¦ęœ€å¤šęœ‰åÆčƒ½č¾¾åˆ° 100 * n,ę•°ę®čŒƒå›“čæ‡å¤§č€Œäøę–¹ä¾æčæ›č”ŒåŠØę€č§„åˆ’,åÆä»„č€ƒč™‘čƄ问题ēš„åƹ偶问题。即ē»Ÿč®”äŗŗę•°ęœ€å¤šäøŗ G ēš„ę–¹ę”ˆę•°,å‡åŽ»åˆ©ę¶¦å°äŗŽ P,äø”ē»Ÿč®”äŗŗę•°ęœ€å¤šäøŗ G ēš„ę–¹ę”ˆę•°。
  2. åƹäŗŽē¬¬äø€éƒØ分,åŠØę€č§„åˆ’ēš„ēŠ¶ę€äøŗ s(i,j),č”Øē¤ŗ考虑äŗ†å‰ i äøŖč®”åˆ’,参äøŽäŗŗꕰäøŗ j ēš„ę–¹ę”ˆę•°ę˜Æ多少。åƹäŗŽē¬¬ i äøŖč®”åˆ’,s(i,j)=s(i,j)+s(i1,jgroup[i])。初始值 s(0,0)=1。äŗŗę•°ęœ€å¤šäøŗ G ēš„ę–¹ę”ˆę•°äøŗ j=0Gs(n,j)。实际äøŠåÆ仄ēœē•„ꎉē¬¬äø€ē»“。
  3. åƹäŗŽē¬¬äŗŒéƒØ分,åŠØę€č§„åˆ’ēš„ēŠ¶ę€äøŗ f(i,j,k),č”Øē¤ŗ考虑äŗ†å‰ i äøŖč®”åˆ’,参äøŽäŗŗꕰäøŗ j ēš„ę–¹ę”ˆę•°,äø”åˆ©ę¶¦äøŗ k ēš„ę–¹ę”ˆę•°ę˜Æ多少。åƹäŗŽē¬¬ i äøŖč®”åˆ’,f(i,j,k)=f(i,j,k)+f(i1,jgroup[i],kprofit[i])。初始值 f(0,0,0)=1。åˆ©ę¶¦å°äŗŽ P,äø”ē»Ÿč®”äŗŗę•°ęœ€å¤šäøŗ G ēš„ę–¹ę”ˆę•°äøŗ j=k=0j<=G,k<Pf(n,j,k)。实际äøŠåÆ仄仍ē„¶ēœē•„ꎉē¬¬äø€ē»“。
  4. ꜀ē»ˆē­”ę”ˆå°±ę˜Æäø¤éƒØ分做差。


X. Video
花花酱 LeetCode 879. Profitable Schemes - åˆ·é¢˜ę‰¾å·„ä½œ EP212

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) å°ŗå–ę³• (11) ꌑꈘē؋åŗč®¾č®”ē«žčµ› (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) ē©·ē«­ęœē“¢ (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) č“ę”„ęÆ (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts