代码随想录Day34 | Leetcode 1005、134、135
一、1005 K次取反后最大化的数组和
题目链接:1005 K次取反后最大化的数组和
核心:要求取反后的数组和最大,也就是取反的元素最好是负数,没有负数的情况下必须是最小的正数(包括0)。
首先,原数组按照绝对值从大到小排列;
然后,遍历数组元素,没遇到负数则将其取反,同时k–;
接着,遍历完所有元素后,k仍大于0需要对最小正数反复取反,若k是偶数由于可以反复取反,实质与不操作没有区别,因此只有k(剩余的k)是奇数时,对最小整数取一次反即可。
最后,统计数组和。
static bool cmp(int a,int b)
{//比较函数,按照绝对值从大到小排列
return abs(a)>abs(b);
}
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end(),cmp); //nums元素按绝对值从大到小排列
for(int i=0;i<nums.size();++i)
{//遍历数组元素,遇到负数则变为正数,同时k--
if(nums[i]<0 && k>0)
{
nums[i]*=-1;
k--;
}
}
//处理完数组中所有负数后k仍大于0,需要对最小的正数(包括0)反复变为负数
if(k%2==1)
nums[nums.size()-1]*=-1; //k为偶数可反复对最小正数操作,相当于没有操作
int res=0;
for(int num:nums)
res+=num;
return res;
}
二、134 加油站
题目链接:134 加油站
核心:建模成剩余汽油gas-cost的问题会简单很多,如果全部的剩余汽油都小于0那么肯定无法环路行驶;否则一定可以环路行驶,只需找到起始位置start即可。
如果从start开始统计剩余汽油和,环路的整个区间内都大于等于0,说明start就是起始位置,也就是说一旦某区间的剩余汽油和小于0,说明从此处开始不能环路行驶,需要更新起始位置。(起始位置必然存在的前提是:全部的剩余汽油和大于等于0)
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
//贪心算法:统计每个元素的gas-cost,一旦某区间累加和小于0,则更新起始位置
int start=0; //记录更新后的起始位置
int total=0; //记录所有元素的gas-cost
int curSum=0; //记录从i开始某一区间的累加和
for(int i=0;i<gas.size();++i)
{
total+=gas[i]-cost[i];
curSum+=gas[i]-cost[i];
if(curSum<0)
{
start=i+1; //更新区间起点,直到从start开始的某区间累加和大于等于0
curSum=0; //重新统计区间元素和
}
}
if(total<0)
return -1; //剩余总和小于说明一定不存在起始位置,否则一定存在起始位置,即start
return start;
}
三、135 分发糖果
题目链接:135 分发糖果
核心:使用2次贪心策略,第一次从前往后遍历,比较右孩子是否大于左孩子,若大于则右孩子比左孩子多一个糖果;第二次从后往前遍历,比较左孩子是否大于右孩子,若大于则左孩子需要更新,要么比右孩子多一个糖果,要么仍沿用第一次遍历得到的左孩子,取其最大值同时满足两次贪心策略。
int candy(vector<int>& ratings) {
//2次贪心策略:从前往后比较右孩子是否大于左孩子,从后往前比较左孩子是否大于右孩子
vector<int> candynum(ratings.size(),1); //定义初始化存放每个小孩的糖果的数组
//从前往后遍历,比较右孩子是否大于左孩子
for(int i=1;i<ratings.size();++i)
{
if(ratings[i]>ratings[i-1])
candynum[i]=candynum[i-1]+1; //右孩子比左孩子多一个
}
//从后往前遍历,比较左孩子是否大于右孩子
for(int i=ratings.size()-1;i>=1;--i)
{
if(ratings[i-1]>ratings[i])
candynum[i-1]=max(candynum[i]+1,candynum[i-1]); //左孩子比右孩子多一个,或者从前往后遍历的糖果,取最大值
}
int res=0;
for(int num:candynum)
res+=num; //统计糖果数
return res;
}