算法训练营day33_贪心算法(3.6)
1005.k次取反后最大化的数组和
先排个序;
如果k<=负数个数,从小到大修改负数->正数,修改k次;
如果k>负数个数,那么多出的次数如果是偶数,就不用管(这时候都是正值,一直逮着一个薅,还是原来的值);如果多出的次数是奇数,那就得找个最小的正数->负数(我刚开始掉坑里了,我直接找原数组里最小的正数,第一可能原数组中没有正数,第二若原数组里有负数,可能最小正数是负数最大值转化而来的);
**随想录思路:**改一下排序规则,绝对值从大到小排序,遇到负值就改正数,有多出的k就薅最小的数,这个思路很好,可以直接得出负数改为正数后处于什么位置,我那个没有办法得出,我是通过初始在数组的位置判断的,get了;
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
int n=nums.size();
int cnt=0,ans=0;
for(int i=0;i<n;i++){
if(nums[i]<0) cnt++;
}
sort(nums.begin(),nums.end());
if(k<=cnt){
for(int i=0,t=0;i<n;i++){
if(t<k){
nums[i]=-nums[i];
t++;
}
ans+=nums[i];
}
}
else{
for(int i=0;i<n;i++){
ans+=abs(nums[i]);
}
if((k-cnt)&1){
int minm=1e9;
if(cnt<n) minm=min(minm,nums[cnt]);
if(cnt>0) minm=min(minm,abs(nums[cnt-1]));
cout<<minm<<endl;
ans-=minm*2;
}
}
return ans;
}
};
134.加油站
由gas,cost数组做差可得一个数组,表示到下一个站油量变化;
如果差数组加和<0,说明油量会在某一点变为负值,显然不合理;
遍历一遍,更新cur(到当前点的油量),如果某一点cur<0,说明它之前的加油站都不能作为起点(这一点我也想到了当时,我当时没想到只需要这一个条件即可),cur刷新为0,加油站更新为i+1;假设最后答案为ans,ans之前的加油站肯定是不能作为起点,ans之后也没有出现cur<0的情况,所以很合理;
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n=gas.size();
int sum=0,cur=0,ans=0;
for(int i=0;i<n;i++){
sum+=gas[i]-cost[i];
cur+=gas[i]-cost[i];
if(cur<0){
cur=0;
ans=i+1;
}
}
if(sum<0) return -1;
return ans;
}
};
135.分发糖果
这道题之前做过类似的;
每个人至少一个,故创建数组要初始化为1个,相邻包括左相邻,右相邻;
先考虑左相邻,从左往右遍历,如果比左边数大,值就为左边数+1;
再考虑右相邻,从右往左遍历,如果比右边数大,值就为右边数+1;
由于左右都要满足,最后取左右两个数组的最大值;
class Solution {
public:
int candy(vector<int>& ratings) {
int n=ratings.size();
vector<int> l(n,1),r(n,1);
for(int i=1;i<n;i++){
if(ratings[i]>ratings[i-1]) l[i]=l[i-1]+1;
}
for(int i=n-2;i>=0;i--){
if(ratings[i]>ratings[i+1]) r[i]=r[i+1]+1;
}
int sum=0;
for(int i=0;i<n;i++) sum+=max(l[i],r[i]);
return sum;
}
};