LeetCode每日一题——740. 删除并获得点数
题目描述
给你一个整数数组 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
标签:
动态规划
解题思路
如果这题想到打家劫舍的话,那么就很容易求解出来,因为这题通过转化之后,就能用打家劫舍的思路才实现。
今天这题同样还是考的dp问题。先说说我一开始的思路,我一直想记录最后删除的那个数发现行不通,又想将其转为哈希映射,看看能不能出来,结果还是无功而返。最后看了一下题解,都说这题是跟打家劫舍的思路类似,一看到这个词,本来是一脸懵逼的,自己好像也没遇见过这种模型。后来仔细一想,当初在b站上看侯卫东老师讲动态规划专题课的时候有一题跟这题非常类似————打劫房屋。(当时这题老师讲的思路是:考虑前i栋房子时,每次考虑最后一栋房子偷或者不偷,取最大即可,状态转移方程为:dp[i] = Max(f[i - 1], f[i - 2] + nums[i])),现在细细想来,今天leetcode上的这题还真和打劫房屋太像了。不过这题确实隐藏的有点深。看到题目中说必须删除每个等于 nums[i] - 1 或 nums[i] + 1 的元素。感觉一下子很难看出来,但是我们只要稍加处理,用哈希映射,开一个map数组,将每个出现的数本身作为该数组的下标,该数出现的次数作为该下标对应的值。那么这样一来,我们就构造了一个新的数组,就像一排房子编好了号码一样,这样就不难看出这题跟打家劫舍很类似了。不过这里跟打劫房屋不同的地方,就是某个编号的房子可能有多个,可以删除多次(即可以偷该编号的多个房子),则其状态转移方程可以写成:dp[i] = Max{dp[i - 1], dp[i - 2] + i * map[i]}。
AC代码
1 class Solution { 2 public int deleteAndEarn(int[] nums) { 3 int n = nums.length; 4 5 if (n == 1) { 6 return nums[0]; 7 } 8 9 int[] map = new int[10000 + 10]; 10 11 // 找出最大的那个数,才能够决定最终dp数组的长度 12 int maxv = Integer.MIN_VALUE; 13 14 // 哈希表映射 15 for (int i = 0; i < n; i++) { 16 map[nums[i]]++; 17 maxv = Math.max(maxv, nums[i]); 18 } 19 20 // 打家劫舍; dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); 21 // 转化为本题:dp[i] = Math.max(dp[i - 1], dp[i - 2] + i * nums[i]); 22 23 int[] dp = new int[maxv + 1]; 24 25 // 初始化 26 dp[1] = 1 * map[1]; 27 28 for (int i = 2; i <= maxv; i++) { 29 dp[i] = Math.max(dp[i - 1], dp[i - 2] + (i * map[i])); 30 } 31 32 return dp[maxv]; 33 } 34 }
类似题目
LintCode上的打劫房屋
leetcode链接:https://leetcode-cn.com/problems/delete-and-earn/