1.买卖股票的最佳时机2
代码:(贪心算法)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int sum = 0;
for(int i = 0;i < prices.size() - 1;i++){
sum += max(prices[i + 1] - prices[i],0);
}
return sum;
}
};
思路:题意是,我们可以多次买入股票(在不同的天数),然后让我们求最大利润。
这里的局部最优是:每次都收获正利润;全局最优是:找到最大的利润总和
具体的做法就是:我们可以把 前一天买的股票在今天卖出去获得正利润 的所有利润相加
那为什么可以这么做?你这样不是忽略了我可以隔着好几天再卖股票的情况了吗?
举个例子:假设你第三天买入股票,第六天卖出。那你的总利润就是prices[6] - prices[3]。
其实这个式子的值等价于 (prices[6] - prices[5] )+ (prices[5] - prices[4] ) + (prices[4] - prices[3] ),即我从第三天开始买入股票,接下来每一天,我都卖出前一天的股票,然后买入今天的股票。这正是我的代码里体现的情况。
2.跳跃游戏
代码:(贪心算法)
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover = 0;
if(nums.size() == 1) return true;
for(int i = 0;i <= cover;i++){
cover = max(cover,i + nums[i]);
if(cover >= nums.size() - 1){
return true;
}
}
return false;
}
};
思路:
这道题涉及到很多选择,感觉很麻烦,是不是有很多种情况,这怎么搞?
如果这样想的话,那确实很复杂。但题上只要求我们给出能不能覆盖,也没有问我们怎么覆盖。那我们就可以直接去看“覆盖范围”能否把整个数组长度覆盖就可以了。
这里的局部最优就是:找当前最大的覆盖范围;全局最优:成功覆盖整个数组。
覆盖范围是什么,怎么表示?
覆盖范围就是当前,你所能跳跃的最大范围。我们可以借助一个例子来思考怎么表示——假设你跳到了第3个元素,然后此元素值为5,那就就最远可以跳到3+nums[3]。所以cover为max(cover,i + nums[i])
那你要怎么判断覆盖范围是否超过数组长度了呢?以及你的cover都比数组长度长了的话,你是不是会导致数组越界?
我采用,在每一次循环里,都进行一次if判断,这样如果cover已经可以覆盖数组的话,就直接返回了,不会继续进行循环,也就不会越界访问了;如果没有覆盖数组,那说明cover此时比数组最后的下标要小,更不会越界了。
注意:这里的cover覆盖范围,是从0开始的。所以比较的时候,是和nums.size() - 1 比。
3.跳跃游戏2
代码:
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 1) return 0;
int result = 0; // 最少跳几步
int cur = 0; // 当前的覆盖范围
int next = 0; // 下一步的覆盖范围
for(int i = 0;i <= cur;i++){
next = max(next,i + nums[i]);
if(i == cur){
result++;
cur = next;
if(cur >= nums.size() - 1){
break;
}
}
}
return result;
}
};
思路:
这道题涉及到了求最少的步数,上一道题只涉及到了判断能否覆盖。
这道题要怎么贪?
局部最优为:用最少的步数,找每一次最大的覆盖范围。全局最优为:最少的步数跳完整个数组
那我们怎么选择下一次最大的覆盖范围?
遍历当前的覆盖范围,用next变量来收集当前这一步中,下一次可以涉及的最大覆盖范围。
找到了最大覆盖范围,就要跳到对应的最远的地方吗?
不是的,是在这一步的覆盖范围内,找下一次最大的覆盖范围。
也就是,不关注具体要跳到哪里,而是关注最远的覆盖范围。
你要怎么统计步数?进行“跳跃”?
在当前的覆盖范围已经用完,往前走必须进行“跳跃”的时候。也就是i == cur。我们把对应的变量进行更新,实现“跳跃”。