【第十五周】740. Delete and Earn

来源:互联网 发布:深圳软件企业认定 编辑:程序博客网 时间:2024/06/06 03:14

原题

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: 6Explanation: 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: 9Explanation: 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].

leetCode地址:https://leetcode.com/problems/delete-and-earn/description/

解题思路

题目大意:
有一组数,每个值的范围为[1,10000]。每次可以从数组中选择一个值t,所有的t都会从数组中删去,同时也要删去所有值为t+1与t-1的数。假设这次操作删去了n个t,就可以获得n*t分。现在求将数组完全删除,所能获得的最高分。

这种题目很明显可以使用动态规划来求解,不过如何定义状态是一个重点。
一开始我的思路是这样的:先将数组排序,然后删去所有完全不连续的值(完全不连续的x, 即数组中没有x+1与x-1),这一阶段删去的数都是我们获得的分数;然后剩下的数都是部分连续的,即删去某个数肯定会丢掉其邻居的分数。然后我们对每一个部分连续的数串,都进行动态规划求解,获取到每一个数串的最高分数。
我们将部分连续数串求最高分的问题分离出来,抽象成另一个问题:有一个数组arr[],每相邻的两个数中最多选择1个,求被选择数的最大和。这个问题的动态规划求解思路: 定义状态F(k)为arr[0]到arr[k]的最大和, 状态转移方程: F(k+1) = max( F(k-1) + arr[k+1], F(k) )。这样子问题就解决了。

后来我发现,这样的算法处理还是太麻烦了;而且题目中有一个条件: Each element nums[i] is an integer in the range [1, 10000]. 所有数的取值范围为1-10000;而10000并不是一个非常大的数字——所以我们可以将排序后的数列看成完全连续的,而不用将孤立的数分开处理。具体如何实现呢?对于那些没有邻居的数,都可以看成左右都有邻居“0”,再对整体使用动态规划算法,就能够除去很多不必要的步骤了。因为很明显,为一个数添加邻居“0”对结果肯定是没有影响的,删去这个数,获得的分数还是一样的。

这时我们算法的复杂度是多少呢?排序算法一般为O(nlogn),动态规划算法复杂度为O(n),所以总复杂度为O(nlogn)。还能不能更简化一点呢?我们发现,既然每个数的取值范围为1-10000,那就完全可以使用另外一个排序思路“桶排序”。桶排序的复杂度为O(n);所以总复杂度就降成了O(n)。

代码

class Solution {public:    int deleteAndEarn(vector<int>& nums) {        if (nums.size() == 0) return 0;        int bucket[10001];        memset(bucket, 0, sizeof(bucket));        for (int i : nums) bucket[i]++;        int dp[10001];        memset(dp, 0, sizeof(dp));        dp[1] = bucket[1];        dp[2] = max(dp[1], bucket[2]*2);        for (int i = 3; i <= 10000; i++) {            dp[i] = max(dp[i-2] + bucket[i]*i, dp[i-1]);        }        return dp[10000];    }};

总结

1、动态规划
2、排序算法

原创粉丝点击