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 everyelement 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
numsis at most20000. - Each element
nums[i]is an integer in the range[1, 10000].
博主浪了整整一個聖誕假期,現在也該收收心了,2018了,今年對於博主是很關鍵的一年,有太多的事情要去做,各種小目標需要完成,還有夢想去追逐,又要開始努力啦~在博主停更的這一周半的時間內,收到了網友們的私信和留言催更,請大家放心,2018年博主會繼續堅持下去,繼續追趕進度,雖然一直都沒有完全追上-.-|||,照LeetCode這出題速度,今年題號有望突破一千大關啊,感覺碉堡了有木有,一起為了幸福而奮斗吧~
好了,來做題吧。這道題給了我們一個數組,每次讓我們刪除一個數字,刪除的數字本身變為了積分累積,並且要同時移除之前數的加1和減1的數,但此時移除的數字不累計積分,讓我們求最多能獲得多少積分。博主最開始嘗試的方法是積分大小來排列,先刪除大的數字,但是不對。於是乎,博主發現相同的數字可以同時刪除,於是就是建立了每個數字和其出現次數之間的映射,然后放到優先隊列里,重寫排序方式comparator為數字乘以其出現次數,先移除能產生最大積分的數字,可是還是不對。其實這道題跟之前那道House Robber的本質是一樣的,那道題小偷不能偷相鄰的房子,這道題相鄰的數字不能累加積分,是不是一個道理?那么對於每一個數字,我們都有兩個選擇,拿或者不拿。如果我們拿了當前的數字,我們就不能拿之前的數字(如果我們從小往大遍歷就不需要考慮后面的數字),那么當前的積分就是不拿前面的數字的積分加上當前數字之和。如果我們不拿當前的數字,那么對於前面的數字我們既可以拿也可以不拿,於是當前的積分就是拿前面的數字的積分和不拿前面數字的積分中的較大值。這里我們用take和skip分別表示拿與不拿上一個數字,takei和skipi分別表示拿與不拿當前數字,每次更新完當前的takei和skipi時,也要更新take和skip,為下一個數字做准備,最后只要返回take和skip中的較大值即可,參見代碼如下:
解法一:
class Solution { public: int deleteAndEarn(vector<int>& nums) { vector<int> sums(10001, 0); int take = 0, skip = 0; for (int num : nums) sums[num] += num; for (int i = 0; i < 10001; ++i) { int takei = skip + sums[i]; int skipi = max(skip, take); take = takei; skip = skipi; } return max(skip, take); } };
下面這種解法直接使用sums數組來更新,而沒有使用額外的變量。上面解法中沒有講解這個sums數組,這里的sums實際上相當於建立了數字和其總積分的映射,這里的總積分的計算方法是由數字乘以其出現次數得來的。由於題目中說了每個數字不會超過10000,所以sums的長度可以初始化為10001,然后遍歷原數組,將遇到的數字都累加到該數字在數組中的位置上。然后從sums數組的第三個數字開始遍歷,更新方法跟上面解法的思路很類似,當前的sums[i]值就等於前一個值sums[i-1]和前兩個值sums[i-2]加上當前的sums[i]值中的較大值,其實思想就是在不拿當前數的積分,跟不拿前一個數的積分加上當前的積分之和,取二者中的較大值更新當前值sums[i],參見代碼如下:
解法二:
class Solution { public: 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]; } };
類似題目:
參考資料:
https://discuss.leetcode.com/topic/112807/java-c-clean-code-with-explanation
