1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
思路:
因为本题是需要返回翻转K次,并返回其数组可能最大值。故可以对数组进行一个排序,按小到大进行一个排序,然后判断是否有负数,若有负数,那就一直翻转负数,若没有那就翻转最小的整数,一直翻转即可,以达到局部最优来取得全局最优。
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());//对数组进行一个排序,按照从小到大进行排序
int size = 0;//用来统计有几个负数
for (int i = 0; i < nums.size(); i++)
{
if (nums[i] < 0 && k>0)//这里是对小于0的数进行翻转,并且每翻转一次就减一;size++;
{
nums[i] = -nums[i];
k--;
size++;//用来统计已经翻转了几个数;
}
else break;//因为是已经排完序了,一旦不满足就终止循环
}
if (size < nums.size() && size>0)//因为在这size在数组中的位置是大于0的,只有这样才能退出上面循环,要在这判断是否越界。
{
if (nums[size - 1] < nums[size])//因为size-1是由负数转变而来,所以判断最大的一个负数与最小的一个正数,哪个大,找到最小的哪个并且后面将K次一直翻转这个
size -= 1;
}
else if (size != 0)//这里就是会出现全是负数的情况,并且翻转后,只能翻转最后一个数值,但此时size=nums.size(),所以要减1
{
size -= 1;
}
while (k--)//将K消耗完
{
nums[size] = -nums[size];
}
int result = 0;
for (int i = 0; i < nums.size(); i++)//统计和
{
result += nums[i];
}
return result;
}
};
后对卡哥代码研究,发现好的算法,确实能使代码精简,卡哥直接按照绝对值最大进行排序,然后依次遍历,判断是否有负数,若有就将变为正数,并且k--,遍历完毕,看k是否为奇数,若为奇数,那就对最后一个值翻转,若不为奇数,则直接累加。
if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
上面这一段判断K是奇数和偶数这一段,很难想到。
这道题一开始有一个难点,那就是不知道怎样才能让其循环加油一周,我用了两个for循环来遍历。这里就变成了暴力循环,说实话,暴力循环需要考虑很多小细节,不容易写,并且容易超出时间限制。下面是我自己写的暴力循环。
class Solution {
public:
int allsum=0;//统计油桶剩多少油量
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
vector<int>profitgas(gas.size(),0);
vector<int>Index;
for (int i = 0; i < gas.size(); i++)
{
profitgas[i] = gas[i] - cost[i];
allsum += profitgas[i];
if (profitgas[i] > 0)
Index.push_back(i);
}
if (allsum < 0)return -1;
int result=-1;
for (int i = 0; i < Index.size(); i++)
{
allsum = 0;//重新将其归为0
result = Index[i];
for (int j = Index[i]; j < profitgas.size(); j++)
{
if (allsum >= 0)//从利润大于0的位置依次向后累加,若大于0,则将其累加,否则就说明,不能从此结点到达下一节点。
{
allsum += profitgas[j];
}
else break;
}
for (int k = 0; k < Index[i]; k++)
{
if (allsum >= 0)
{
allsum += profitgas[k];
}
else break;
}
if (allsum >= 0)
{
return result;
break;
}
}
return -1;
}
};
卡哥在这使用了类似买卖股票的最佳时机,因为当前加油站可加油数量-到达下一加油站的数量,就是我经过此加油站我还能有多少油。
这样我不断判断,每个加油站的净利油,依次累加,若大于或者等于0,说明我能够到达下一站点。
在这先求每个站点净利油量,从第一个开始算,若大于0,我们就从这开始,一旦遇到所有近利油小于0,那么我们就更换开始站点,因为说明我们从当前站点开始一定会在此站点发现不能到达下一站点。只要在遍历完数组,发现一直净利油大于0说明,当前站点就是我们要找的站点。
可能大家会有疑惑,为什么呢?其实大家仔细看题目就发现因为题目答案是唯一的,如果是从当前站点开始,说明在当前站点之前都不行,也就是小于0,但是存在一个唯一值,是不是从当前站点开始后面的都要大于0,这样两者相加才能大于或等于0,那么此站点是唯一的。
最后判断所有站点净利油是否大于等于0,若不是,则返回-1,说明从任何站点开始都没有用。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost)
{
int allsum = 0;
int curprofit = 0;
int result=0;
for (int i = 0; i < gas.size(); i++)
{
allsum += gas[i] - cost[i];
curprofit += gas[i] - cost[i];
if (allsum < 0)
{
result = i + 1;
allsum = 0;
}
}
if (curprofit < 0)return -1;
return result;
}
};
本题确实有难度,因为要考虑多维度的想法,思路:先考虑好一个维度,再基于此维度去考虑另一个维度。
因为要使得糖果数量最小,我们可以先从左边遍历,依次分发,按照规则来分发。
从左边遍历之后,再从右边遍历,按照从右到左进行分发,然后当前糖果值取第一次从左边分发到从右边分发的最大值,这里其实不知道为什么要取最大值,但是感觉是得取最大值才能符合。
如果取最小值,说明我只能满足其中一种,但是另一种无法满足条件。
所以只能取最大值才能满足。
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int>candy1(ratings.size(),1);
int result = 0;
for (int i = 1; i < ratings.size(); i++)
{
if (ratings[i] > ratings[i - 1])
{
candy1[i] = candy1[i - 1] + 1;
}
}
for (int i = ratings.size() - 2; i >= 0; i--)
{
if (ratings[i ] > ratings[i+1])
{
candy1[i - 1] = max(candy1[i+1] + 1, candy1[i]);
}
}
for (int i = 0; i < candy1.size(); i++)
{
result += candy1[i];
}
return result;
}
};