LeetCode 1005 K次取反后最大化的数组和
题目链接:https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/
思路:
贪心:
局部最优:将负数变为正数,当前数值达到最大。
整体最优:整个数组和达到最大。
当负数全变为正数后,k仍大于0,此时就需要将最小的正数反复取反
局部最后:将最小的正数取反,当前数值达到最小。
整体最优:整个数组和达到最大。
代码:
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end(),cmp); // 将所有数按绝对值大小排列
// 如果有负数,那么将负数变正
for(int i = 0;i<nums.size();i++)
{
if(nums[i]<0&&k>0)
{
nums[i] *= -1;
k--;
}
}
// 如果负数全变正后,K仍不为0,那么就将最小的正数反复变化即可
// 因为一开始是按绝对值排列的,所以最小的正数一定在数组的最后面
if(k%2==1) nums[nums.size()-1] *= -1; // 当负数全部取反后,如果K是奇数,那么就将最后一个数取反
// 求和
int result = 0;
for(int a:nums)
result += a;
return result;
}
// 快排的比较函数要是静态函数
static bool cmp(int a,int b)
{
return abs(a)>abs(b);
}
};
自己的代码
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
while(k--)
{
nums[0] = -nums[0];
sort(nums.begin(),nums.end());
}
int result = 0;
for(int i = 0;i<nums.size();i++)
{
result += nums[i];
}
return result;
}
};
总结
能想到贪心的思路,但是在写的时候不知道要怎么写,所以就采用每次都快排一次,然后将nums[0]取反,然后k--,一直循环到k为0。
LeetCode 134. 加油站
题目链接:https://leetcode.cn/problems/gas-station/
思路:
贪心:
首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明各个站点的加油站的剩油量rest[i]相加一定是大于等于零的。
设每个加油站的剩余量rest[i]为gas[i] - cost[i]。
i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。
代码:
暴力解法 O()
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
for(int i = 0;i<gas.size();i++)
{
int rest = gas[i]-cost[i];
int index = (i+1)%gas.size();
// 模拟以i为起点绕行一圈
while(rest>0&&index!=i)
{
rest += gas[index]-cost[index];
index = (index+1)%gas.size(); // 当index>cost.size()时,此时index%cost.size()得到的值就是从1开始的值
}
if(rest>=0&&index==i)
return i;
}
return -1;
}
};
贪心O(n)
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) // 一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油
{
start = i+1; // 此时只能从i+1开始出发
cursum = 0;
}
}
if(totalsum<0) return -1;
return start;
}
};
总结
贪心解法还需要多理解。
LeetCode 135 分发糖果
题目链接:https://leetcode.cn/problems/candy/
思路:
本题有两种情况都要处理:左边孩子比右边孩子大和右边孩子比左边孩子大。
此时需要先处理一种情况,再处理第二种情况。
注:处理右边孩子比左边孩子大时必须从后往前遍历
代码:
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int>candy(ratings.size(),1);
// 先处理第一种情况,右边孩子比左边孩子大
for(int i = 1;i<ratings.size();i++)
{
if(ratings[i]>ratings[i-1])
candy[i] = candy[i-1]+1;
}
// 此时处理第二种情况,左边孩子比右边孩子大,此时必须从后往前遍历
for(int i = ratings.size()-2;i>=0;i--)
{
if(ratings[i]>ratings[i+1])
candy[i] = max(candy[i],candy[i+1]+1); // candy[i]取值时要同时考虑上第一种情况
}
int result = 0;
for(int a:candy)
result += a;
return result;
}
};
总结
学习到了如何处理存在两种比较情况时的方法。
今日总结:
贪心算法还是很难,需要多记多理解。