【LeetCode】LeetCode之删除并获得点数——动态规划、排序+动态规划
🔗【LeetCode】LeetCode之打家劫舍【暴力遞歸、動態規劃、動態規劃之優化空間的具體分析與實現】
🔗【LeetCode】LeetCode之打家劫舍Ⅱ——暴力遞歸+動態規劃解決循環問題+DP空間優化
1.題目描述
給你一個整數數組 nums ,你可以對它進行一些操作。
每次操作中,選擇任意一個 nums[i] ,刪除它并獲得 nums[i] 的點數。之后,你必須刪除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
開始你擁有 0 個點數。返回你能通過這些操作獲得的最大點數。
示例 1:
輸入:nums = [3,4,2]
輸出:6
解釋:
刪除 4 獲得 4 個點數,因此 3 也被刪除。
之后,刪除 2 獲得 2 個點數。總共獲得 6 個點數。
示例 2:
輸入:nums = [2,2,3,3,3,4]
輸出:9
解釋:
刪除 3 獲得 3 個點數,接著要刪除兩個 2 和 4 。
之后,再次刪除 3 獲得 3 個點數,再次刪除 3 獲得 3 個點數。
總共獲得 9 個點數。
提示:
- 1 <= nums.length <= 2 * 104
- 1 <= nums[i] <= 104
2.思路分析Ⅰ
根據題目求最大點數,我們首先能夠想到的就是使用動態規劃求解。通過迭代+記憶集能夠解題。
思考該題你會發現,無論怎么想很難直接轉換為動態規劃求解,那么我們只有使用間接的方式【明面上看不出來】,那我們如何向動態規劃的一般思路靠攏呢?
平常最常見的動態規劃一般都是類似于這種dp[n]=max(dp[n-1],dp[n-2]+a);此時我們會發現題目中給定了一個類似的條件:刪除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素,但是后者是具體的值,而前者是下標。這也是一個問題!
根據上述過程分析,主要使用動態規劃解題的一個問題是題目中的條件是具體的值-1、+1,而動態規劃的一般方式是索引-a、+a。
怎么將值轉換為索引呢?
很簡單,構建一個新數組即可,將舊數組的每一個值充當新數組的索引。當然這個新數組的大小是要根據舊數組的最大值確定的。
那么新數組的索引存儲什么呢?
如果某個數據只有一份,那么它在新數組中的索引和值是一致的。如果數據有n份,那么它的索引就是單個值,它的值是n*單個值。
比如[2,2,2],那么它存儲在索引為2的位置,值為6.
3.動態規劃解題
下面的rob方法就和打家劫舍一樣了:打家劫舍
public int deleteAndEarn(int[] nums) {int maxNum = -1;for (int num : nums) { //求出原數組中的最大值maxNum = Math.max(maxNum, num);}int[] sums = new int[maxNum + 1];//根據最大值構建新數組for (int i = 0; i < nums.length; i++) {sums[nums[i]] += nums[i];}return rob(sums);}//rob打家劫舍解題思路一樣public int rob(int[] sums) {int size = sums.length;int pre = sums[0];int suf = Math.max(pre, sums[1]);int maxEarn = suf;for (int i = 2; i < size; i++) {maxEarn = Math.max(pre + sums[i], suf);pre = suf;suf = maxEarn;}return maxEarn;}復雜度分析
時間復雜度:O(n * m)m為nums數組中的最大值
空間復雜度:O(m)
4.思路分析Ⅱ
說到底本題不能直接使用動態規劃的原因就是你無法保證前面操作的數據是否會對后續造成影響,比如說[2,3,4,1,6,7,1],你如果要操作2,那么數組中的3和1都要進行刪除,而這3和1是由于無法確定位置,我們很難在迭代過程中對其進行聯系的。另外可以通過排序+劃分子序列來完成
例如數組為[2,4,3,1,9],那么它的執行流程為
1.排序:[1,2,3,4,9]
2.劃分子序列 [最大間隔為1] – >[1,2,3,4]與[9]
4.排序+動態規劃
注意到若 nums 中不存在某個元素 x,則選擇任一小于 x 的元素不會影響到大于 x 的元素的選擇。因此我們可以將nums 排序后,將其劃分成若干連續子數組,子數組內任意相鄰元素之差不超過 11。對每個子數組按照方法一的動態規劃過程計算出結果,累加所有結果即為答案。
public int deleteAndEarn3(int[] nums) {Arrays.sort(nums);List<Integer> list = new ArrayList<>();list.add(nums[0]);int result = 0;int size = 1;for (int i = 1; i < nums.length; i++) {int val = nums[i];if (val == nums[i - 1]) {list.set(size - 1, list.get(size - 1) + val);} else if (nums[i - 1] + 1 == val) {list.add(val);++size;} else {result += rob3(list);list.clear();list.add(nums[i]);size = 1;}}result += rob3(list);return result;}private int rob3(List<Integer> list) {int size = list.size();if (size == 1) {return list.get(0);}int pre = list.get(0);int suf = Math.max(pre, list.get(1));int maxEarn = suf;for (int i = 2; i < size; i++) {maxEarn = Math.max(pre + list.get(i), suf);pre = suf;suf = maxEarn;}return maxEarn;}復雜度分析:
時間復雜度:O(n*logn),排序花費的時間
空間復雜度:O(n)
總結
以上是生活随笔為你收集整理的【LeetCode】LeetCode之删除并获得点数——动态规划、排序+动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LeetCode】LeetCode之打
- 下一篇: 【LeetCode】LeetCode之跳