第八章 贪心算法part03
1005. K 次取反后最大化的数组和
给你一个整数数组 nums
和一个整数 k
,按以下方法修改该数组:
- 选择某个下标
i
并将nums[i]
替换为-nums[i]
。
重复这个过程恰好 k
次。可以多次选择同一个下标 i
。
以这种方式修改数组后,返回数组 可能的最大和 。
思路:考虑所有可能,直接写,一开始还是很不完善,改了很多次每次都有失败测试用例,情况反复调整最后才ac。
正负数分布情况(①全为负数注意最后一个的操作,②全为正数只操作第一个数,③负数个数大于k值全部取反,④负数个数小于k值,考虑最后一个负数与第一个整数绝对值大小,⑤遇到0直接结束)
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
int sum=0;
//分情况讨论
//最小数值为正,只对最小数值进行操作
if(nums[0]>0) {
nums[0]=(k%2==0?nums[0]:-nums[0]);
for(int i=0;i<nums.size();i++) sum+=nums[i];
return sum;
}
for(int i=0;i<nums.size()&&k>0;i++){
//数值中有正有负,且负数个数小于k
if(i>=1&&nums[i]>0){
if(nums[i]>nums[i-1]) nums[i-1]=(k%2==0?nums[i-1]:-nums[i-1]);
else nums[i]=(k%2==0?nums[i]:-nums[i]);
break;
}
//对负值取反
if(nums[i]<0){
nums[i]=-nums[i];
k--;
//全为负则对绝对值最小的负数多次操作
if(i==nums.size()-1&&k!=0){
nums[i]=(k%2==0?nums[i]:-nums[i]);
break;
}
}
//遇到0完成剩余操作,结果不变
if(nums[i]==0) break;
}
for(int i=0;i<nums.size();i++) sum+=nums[i];
return sum;
}
};
题解比较简洁,是按照绝对值大小对数值进行排序。
class Solution {
static bool cmp(int a,int b){
return abs(a)>abs(b);
}
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
int sum=0;
sort(nums.begin(),nums.end(),cmp);
for(int i=0;i<nums.size();i++){
if(nums[i]<0&&k>0) {
nums[i]*=-1;
k--;
}
}
if(k%2==1) nums[nums.size()-1]*=-1;
for(int i=0;i<nums.size();i++) sum+=nums[i];
return sum;
}
};
134. 加油站
在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1
。如果存在解,则 保证 它是 唯一 的。
思路:想不出来,看了题解。计算每个站剩余汽油的和,如果出现和小于0则说明不可能在前面的站出发,将出发点设为当前节点+1。通过局部最优推出全局最优。
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();
// while(rest>0&&index!=i){
// rest+=gas[index]-cost[index];
// index=(index+1)%gas.size();
// }
// if(rest>=0&&index==i) return i;
// }
// return -1;
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;
else return start;
}
};
135. 分发糖果
n
个孩子站成一排。给你一个整数数组 ratings
表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
思路:按最基础的做法将前后两数比较后去做增加,出现可以处理升序,不能处理降序情况。有想到从右边开始遍历,但是觉得可能重复也没有去实现就看了题解。(还是总是忍不住看题解)
题解是从左边遍历处理升序情况,从右边遍历处理从右往左看升序情况
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> alloc(ratings.size(),1);
for(int i=0;i<ratings.size()-1;i++){
if(ratings[i]<ratings[i+1]){
alloc[i+1]=alloc[i]+1;
}
}
for(int i=ratings.size()-1;i>0;i--){
if(ratings[i]<ratings[i-1]){
alloc[i-1]=max(alloc[i-1],alloc[i]+1);
}
}
int sum=0;
for(int i=0;i<alloc.size();i++){
sum+=alloc[i];
}
return sum;
}
};
不太能跟贪心联系到一起,感觉是正常思路然后做到更多随机应变。