LeetCode 740 - Delete and Earn


https://leetcode.com/problems/delete-and-earn/description/
Given an array nums of integers, you can perform operations on the array.
In each operation, you pick any nums[i] and delete it to earn nums[i] points. After, you must delete every element equal to nums[i] - 1 or nums[i] + 1.
You start with 0 points. Return the maximum number of points you can earn by applying such operations.
Example 1:
Input: nums = [3, 4, 2]
Output: 6
Explanation: 
Delete 4 to earn 4 points, consequently 3 is also deleted.
Then, delete 2 to earn 2 points. 6 total points are earned.
Example 2:
Input: nums = [2, 2, 3, 3, 3, 4]
Output: 9
Explanation: 
Delete 3 to earn 3 points, deleting both 2's and the 4.
Then, delete 3 again to earn 3 points, and 3 again to earn 3 points.
9 total points are earned.
Note:

  • The length of nums is at most 20000.
  • Each element nums[i] is an integer in the range [1, 10000].

  •     int deleteAndEarn(vector<int>& nums) {
            if (nums.empty()) return 0;
            const auto range = minmax_element(nums.begin(), nums.end());
            const int l = *(range.first);
            const int r = *(range.second);        
            vector<int> points(r - l + 1, 0);
            for (const int num : nums)
                points[num - l] += num;
            return rob(points);
        }
    private:
        // From LeetCode 198. House Robber
        int rob(const vector<int>& nums) {
            int dp2 = 0;
            int dp1 = 0;
            for (int i = 0; i < nums.size() ; ++i) {
                int dp = max(dp2 + nums[i], dp1);
                dp2 = dp1;
                dp1 = dp;
            }
            return dp1;
        }
    There are several tricks in the problem.
     -- Since each element nums[i] is within the range of [1, 10000], we can use the bucket sort to sort the nums.
     -- For deleting each nums[i], we can delete all same numbers together. So when we do the bucket sort, we can sum up all the repeated numbers in the same bucket. 
     -- Then the problem is a classic backpack problem. 

    Define take[10001] and skip[10001], where take[i] means for number i, we delete the number i, the max number of points. skip[i] means we don't delete it.

    Then the transit function should be:
    take[i] = skip[i - 1] +  values[i]
    skip[i] = Math.max(take[i - 1], skip[i - 1]);

    http://www.cnblogs.com/grandyang/p/8176933.html
    好了,来做题吧。这道题给了我们一个数组,每次让我们删除一个数字,删除的数字本身变为了积分累积,并且要同时移除之前数的加1和减1的数,但此时移除的数字不累计积分,让我们求最多能获得多少积分。博主最开始尝试的方法是积分大小来排列,先删除大的数字,但是不对。于是乎,博主发现相同的数字可以同时删除,于是就是建立了每个数字和其出现次数之间的映射,然后放到优先队列里,重写排序方式comparator为数字乘以其出现次数,先移除能产生最大积分的数字,可是还是不对。其实这道题跟之前那道House Robber的本质是一样的,那道题小偷不能偷相邻的房子,这道题相邻的数字不能累加积分,是不是一个道理?那么对于每一个数字,我们都有两个选择,拿或者不拿。如果我们拿了当前的数字,我们就不能拿之前的数字(如果我们从小往大遍历就不需要考虑后面的数字),那么当前的积分就是不拿前面的数字的积分加上当前数字之和。如果我们不拿当前的数字,那么对于前面的数字我们既可以拿也可以不拿,于是当前的积分就是拿前面的数字的积分和不拿前面数字的积分中的较大值。这里我们用take和skip分别表示拿与不拿上一个数字,takei和skipi分别表示拿与不拿当前数字,每次更新完当前的takei和skipi时,也要更新take和skip,为下一个数字做准备,最后只要返回take和skip中的较大值即可
    https://zxi.mytechroad.com/blog/dynamic-programming/leetcode-740-delete-and-earn/
    Because all numbers are positive, if we "take" a number (use it to score points), we might as well take all copies of it, since we've already erased all its neighbors. We could keep a count of each number so we know how many points taking a number is worth total.
    Now let's investigate what happens when we add a new number X (plus copies) that is larger than all previous numbers. Naively, our answer would be the previous answer, plus the value of X - which can be solved with dynamic programming. However, this fails if our previous answer had a number taken that was adjacent to X.
    Luckily, we can remedy this. Let's say we knew using, the value of our previous answer, and avoid, the value of our previous answer that doesn't use the previously largest value prev. Then we could compute new values of using and avoid appropriately.
    Algorithm
    For each unique value k of nums in increasing order, let's maintain the correct values of avoid and using, which represent the answer if we don't take or take k respectively.
    If the new value k is adjacent to the previously largest value prev, then the answer if we must take k is (the point value of k) + avoid, while the answer if we must not take k is max(avoid, using). Similarly, if kis not adjacent to prev, the answer if we must take k is (the point value of k) + max(avoid, using), and the answer if we must not take k is max(avoid, using).
    At the end, the best answer may or may not use the largest value in nums, so we return max(avoid, using).
    Our demonstrated solutions showcase two different kinds of sorts: a library one, and a radix sort. For each language, the other kind of solution can be done without much difficulty, by using an array (Python) or HashMap (Java) respectively.
    • Time Complexity (Python): O(N \log N), where N is the length of nums. We make a single pass through the sorted keys of N, and the complexity is dominated by the sorting step.
    • Space Complexity (Python): O(N), the size of our count.
    • Time Complexity (Java): We performed a radix sort instead, so our complexity is O(N+W) where W is the range of allowable values for nums[i].
    • Space Complexity (Java): O(W), the size of our count.
      public int deleteAndEarn(int[] nums) {
        int[] count = new int[10001];
        for (int x : nums)
          count[x]++;
        int avoid = 0, using = 0, prev = -1;

        for (int k = 0; k <= 10000; ++k)
          if (count[k] > 0) {
            int m = Math.max(avoid, using);
            if (k - 1 != prev) {
              using = k * count[k] + m;
              avoid = m;
            } else {
              using = k * count[k] + avoid;
              avoid = m;
            }
            prev = k;
          }
        return Math.max(avoid, using);

      }
    https://leetcode.com/problems/delete-and-earn/discuss/109895/JavaC++-Clean-Code-with-Explanation
    1. If we sort all the numbers into buckets indexed by these numbers, this is essentially asking you to repetitively take an bucket while giving up the 2 buckets next to it. (the range of these numbers is [1, 10000])
    2. The optimal final result can be derived by keep updating 2 variables skip_itake_i, which stands for:
      skip_i : the best result for sub-problem of first (i+1) buckets from 0 to i, while you skip the ith bucket.
      take_i : the best result for sub-problem of first (i+1) buckets from 0 to i, while you take the ith bucket.
    3. DP formula:
      take[i] = skip[i-1] + values[i];
      skip[i] = Math.max(skip[i-1], take[i-1]);
      take[i] can only be derived from: if you skipped the [i-1]th bucket, and you take bucket[i].
      skip[i] through, can be derived from either take[i-1] or skip[i-1], whatever the bigger;
     * for numbers from [1 - 10000], each has a total sum sums[i]; if you earn sums[i], you cannot earn sums[i-1] and sums[i+1]
     * kind of like house robbing. you cannot rob 2 connected houses.
        public int deleteAndEarn(int[] nums) {
            int n = 10001;
            int[] values = new int[n];
            for (int num : nums)
                values[num] += num;
    
            int take = 0, skip = 0;
            for (int i = 0; i < n; i++) {
                int takei = skip + values[i];
                int skipi = Math.max(skip, take);
                take = takei;
                skip = skipi;
            }
            return Math.max(take, skip);
        }

    https://leetcode.com/problems/delete-and-earn/discuss/109871/Awesome-Python-4-liner-with-explanation-Reduce-to-House-Robbers-Question
    The condition that we cannot pick adjacent values is similar to the House Robber question that we cannot rob adjacent houses. Simply pass points into the robfunction for a quick win

    https://leetcode.com/problems/delete-and-earn/discuss/109889/Java-Easy-DP-Solution
    Time: O(M+N)
    Space: O(N)
    M: the length of input array
    N: the range of the value of each int element

    public int deleteAndEarn(int[] nums) {
        int[] count = new int[10001];
        for(int n : nums){
            count[n] += n;
        }
        int[] dp = new int[10003];
        for(int i = 10000; i >= 0; i--) {
            dp[i] = Math.max(count[i] + dp[i + 2], dp[i + 1]);
        }
        return dp[0];
    }



        public int deleteAndEarn(int[] nums) {
            int[] count = new int[10001];
            for (int x: nums) count[x]++;
            int avoid = 0, using = 0, prev = -1;

            for (int k = 0; k <= 10000; ++k) if (count[k] > 0) {
                int m = Math.max(avoid, using);
                if (k - 1 != prev) {
                    using = k * count[k] + m;
                    avoid = m;
                } else {
                    using = k * count[k] + avoid;
                    avoid = m;
                }
                prev = k;
            }
            return Math.max(avoid, using);
        }
    class Solution(object):
        def deleteAndEarn(self, nums):
            count = collections.Counter(nums)
            prev = None
            avoid = using = 0
            for k in sorted(count):
                if k - 1 != prev:
                    avoid, using = max(avoid, using), k * count[k] + max(avoid, using)
                else:
                    avoid, using = max(avoid, using), k * count[k] + avoid
                prev = k
            return max(avoid, using)


    下面这种解法直接使用sums数组来更新,而没有使用额外的变量。上面解法中没有讲解这个sums数组,这里的sums实际上相当于建立了数字和其总积分的映射,这里的总积分的计算方法是由数字乘以其出现次数得来的。由于题目中说了每个数字不会超过10000,所以sums的长度可以初始化为10001,然后遍历原数组,将遇到的数字都累加到该数字在数组中的位置上。然后从sums数组的第三个数字开始遍历,更新方法跟上面解法的思路很类似,当前的sums[i]值就等于前一个值sums[i-1]和前两个值sums[i-2]加上当前的sums[i]值中的较大值,其实思想就是在不拿当前数的积分,跟不拿前一个数的积分加上当前的积分之和,取二者中的较大值更新当前值sums[i]
        int deleteAndEarn(vector<int>& nums) {
            vector<int> sums(10001, 0);
            for (int num : nums) sums[num] += num;
            for (int i = 2; i < 10001; ++i) {
                sums[i] = max(sums[i - 1], sums[i - 2] + sums[i]);
            }
            return sums[10000];
        }
    


    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