1.K次取反后最大的数组和
代码:(贪心算法)
class Solution {
public:
static bool cmp(int a,int b){
return abs(a) > abs(b);
}
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end(),cmp); // 把数组按照绝对值大小排序
int sum = 0;
for(int i = 0;i < nums.size();i++){
if(nums[i] < 0 && k > 0){ // 遇到负数,就把它当成负数相加
k--;
nums[i] = 0 - nums[i];
}
sum += nums[i];
}
if(k % 2 == 1){
sum = sum - nums[nums.size() - 1] + (0 - nums[nums.size() - 1]);
// 1 2 3 -6
// sum = sum - (-6) + (0 - (-6))
}
return sum;
}
};
思路:
这道题怎么贪?
先把数组里的负数,按照绝对值大小排序。遇到负数,就耗费k一次,把它变成正数。
局部最优为:尽可能地加更多的正数;全局最优:总和最大
那如果遍历了一次数组,k还没用完怎么办?
局部最优:取绝对值最小的数来切换正负,消耗完所有的k;全局最优:使得正负的改变,对总和的影响最小。
2.加油站
代码:(贪心算法)
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int cursum = 0;
int totalsum = 0;
int start = 0;
for(int i = 0;i < gas.size();i++){
cursum += gas[i] - cost[i];
totalsum += gas[i] - cost[i];
if(cursum < 0){
start = i + 1;
cursum = 0;
}
}
if(totalsum < 0) return -1;
return start;
}
};
思路:
这道题怎么贪?
局部最优是:当前净油量必须大于0才能继续行驶;全局最优:成功从起始位置到终止位置
当前的“净油量”是什么?
就是目前走过的每个站点 加的油量 - 消耗的油量。
你怎么找起始位置?怎么判断是否能走完全程?
起始位置start,通过我们的净油量来看。如果净油量已经小于0,代表这个站点,以及之前的站点都不能作为起始位置,选择下一站作为我们的起始位置。这样遍历一圈,看看最后start最后是多少。(而且题上说:如果存在解,则 保证 它是 唯一 的。所有求到的start就是唯一的。)
判断是否有解,是通过累计所有站点的净油量totalsum。 如果小于0,说明不可能走完全程。
3.分发糖果
代码:
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> result(ratings.size() ,1);
int sum = 0;
// 从左到右遍历 如果右边大于左边,则其分数要比左边多1
for(int i = 1;i < ratings.size();i++){
if(ratings[i - 1] < ratings[i]){
result[i] = result[i - 1] + 1;
}
}
// 从右到左遍历 如果左边大于右边,则其分数要比右边多1
for(int i = ratings.size() - 1; i > 0;i--){
if(ratings[i - 1] > ratings[i]){
result[i - 1] = max(result[i] + 1,result[i - 1]);
}
}
// 计算糖果总和
for(int i = 0;i < result.size();i++){
sum += result[i];
}
return sum;
}
};
思路:
这种涉及到考虑多个因素的题,要一个一个来考虑,不要想着一次性全做好(人生也是这样
那你怎么取分别考虑呢?
我第一次从左到右遍历数组,如果右边大于左边的分数,那就让其糖果数比左边的多一个。
第二次从右到左遍历数组,如果左边大于右边的分数,那就让其糖果数比右边的多一个(这个数,要和上一次得到的糖果数取一个最大值)。
为什么第二次的遍历顺序是从右到左遍历呢?
因为 rating[5]与rating[4]的比较 要利用上 rating[5]与rating[6]的比较结果,所以 要从后向前遍历。