1.删除并获得点数
题目描述:
给你一个整数数组 nums ,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数
思路:
注意到若 \textit{nums}nums 中不存在某个元素 xx,则选择任一小于 xx 的元素不会影响到大于 xx 的元素的选择。因此我们可以将 \textit{nums}nums 排序后,将其划分成若干连续子数组,子数组内任意相邻元素之差不超过 11。对每个子数组按照方法一的动态规划过程计算出结果,累加所有结果即为答案。
代码:
int rob4(vector<int>sum) {
int n = sum.size();
if (n == 1)
return sum[0];
if (n == 2)
return max(sum[0], sum[1]);
int first=sum[0],second=max(sum[0], sum[1]);
for (int i = 2; i < n; i++) {
int tmp = second;
second = max(first + sum[i], tmp);
first = tmp;
}
return max(first,second);
}
int deleteAndEarn(vector<int>& nums) {
int n = nums.size();
if (n == 1)
return nums[0];
sort(nums.begin(), nums.end());//排序
int ans=0;
//求相同元素的和到sum中
vector<int>sum = { nums[0] };
for (int i = 1; i < n; ++i) {
int val = nums[i];
if (val == nums[i - 1]) {
sum.back() += val;
} else if (val == nums[i - 1] + 1) {
sum.push_back(val);
} else {
ans += rob4(sum);//防止短片
sum = {val};//不断更新sum数组
}
}
ans += rob4(sum);
return ans;
}
2.跳跃游戏II
题目描述:
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
思路:
如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。
例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。
从下标 1 出发,最远可到达下标 4。下标 1 可到达的位置中,下标 4 的值是 4 ,从下标 4 出发可以达到更远的位置,因此第二步到达下标 4。
代码:
int jump(vector<int>& nums) {
int n=nums.size();
int step=0,maxPosition=0,end=0;
for(int i=0;i<n-1;i++){
maxPosition=max(maxPosition,i+nums[i]);
if(i==end){//到达了end,开始再跳更新end
end=maxPosition;
step++;
}
}
return step;
}
3.零钱兑换
题目描述:
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
思路:
关键是动态规划方程的设计
我按我的思路一步步来吧
首先很容易想到使用dp[i]代表金额为i时,需要的最小硬币数量,最终求的是dp[amount]
然后试者填一下
dp = [-1]*(amount+1)
dp[0] = 0
dp[存在硬币的面值] = 1
如 coins = [1,2,5]
那dp[1],dp[2],dp[5] = 1,1,1
这些是初始条件,下面开始按顺序填表
在填写dp[i]的时候,其实就是假设在满足dp[i]的情况下,拿走一个硬币时需要的个数,然后+1
拿走硬币是什么意思呢?就是dp[i-coin],coin是选取的硬币的面值,如果dp[i-coin]!=-1,说明dp[i-coin]有解
那么dp[i-coin+coin]也就有解了,那么需要的硬币数量就是dp[i-coin]+1
但是有可能有多个解,这个时候就要找到最小的那个,即使用硬币最少的那个,所以最后状态转移方程就是这样的:
dp[i] = min(dp[i-coin[0]],dp[i-coin[1]]...,dp[i-coin[j]])+1
其中0-j属于dp[j]!=-1
代码实现:
//动态规划dp[i]表示凑成i所需要的硬币个数
//dp[i]=min(dp[i-coins[0]],dp[i-coins[1],...,dp[i-coins[n-1]]])+1
int dp[10050];//代表组合i元钱所需要的最小硬币数
int coinChange(vector<int>& coins, int amount) {//就跟走楼梯一样
sort(coins.begin(),coins.end());
dp[0] = 0;
int n=coins.size();
for (int i = 1 ; i <= amount ;i++)
{
dp[i] = INT_MAX;
for (int j = 0 ; j < n;j++)
{
if(i-coins[j]>=0&&dp[i-coins[j]]!=INT_MAX)
dp[i] = min(dp[i],dp[i-coins[j]] + 1);
}
}
return dp[amount] == INT_MAX ? -1:dp[amount];
}
4.最大子序和
题目描述:
给你一个整数数组
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
思路和算法:
代码实现:
//动态规划
int maxSubArray(vector<int>& nums) {
int pre = 0, maxAns = nums[0];
for (int x : nums) {
pre = max(pre + x, x);//小就跟更新
maxAns = max(maxAns, pre);//同时记录最大的
}
return maxAns;
}
5.环形子数组的最大和
题目描述:
给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。
在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i < A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] = C[i])
此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], ..., C[j],不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length)。
代码:
int maxSubarraySumCircular(vector<int>& nums) {
int dp = nums[0], Max = dp, sum = dp, Min = 0;
int n = nums.size();
for (int i = 1; i < n; i++) {
sum += nums[i];
dp = nums[i] + max(dp, 0);
Max = max(Max, dp);
}
dp = nums[0];
for (int i = 1; i < n - 1; i++) {
dp = nums[i] + min(0, dp);
Min = min(dp, Min);
}
return max(sum - Min, Max);
}