K次取反后最大化的数组和
题目链接:力扣
第一眼的想法是去分类讨论
先将总的元素做sort排序
当元素全大于0的时候,则反转数组中最小的元素,k为负值的情况下,最小元素反转,k为偶数的情况下,最小元素不需处理(因为反转偶数次还是原来的元素)
当元素含小于0的时候,总体思路是先将负数的反转为正数,接着若负数的反转完了可是k还有剩余的反转次数,则 反转 【原来是负数现在反转为正数 和 第一个原来就是正数的数 中 较小的那一个数】,并反复反转该数,直到k的值用完。
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
int res = 0;
if(nums[0]>0) //此时全员为正数
{
if(k%2) //k为奇数 , k为偶数时不处理
nums[0] = -nums[0]; //最小值取反
}
else if(nums[0]<0) //nums含负数
{
int temp = k;
int i = 0;
while(temp)
{
cout<<temp<<endl;
if(nums[i]<=0)
{
nums[i]=-nums[i];
i=i+1<nums.size()? i+1:i;
temp--;
} //将负数转为正数
else if(nums[i]>0 && nums[i-1]<=nums[i] && temp>0)
{nums[i-1] = -nums[i-1]; temp--;}
else if(nums[i]>0 && nums[i-1] > nums[i] && temp>0)
{nums[i] = -nums[i]; temp--;}
}
}
for(int i = 0; i < nums.size();i++)
res+=nums[i];
return res;
}
};
卡哥给出的思路更加简洁:
贪心的思路,
局部最优:让绝对值大的负数变为正数,当前数值达到最大。
整体最优:整个数组和达到最大。
解题步骤:
- 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
- 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
- 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
- 第四步:求和
-
class Solution { static bool cmp(int a, int b) { return abs(a) > abs(b); } public: int largestSumAfterKNegations(vector<int>& A, int K) { sort(A.begin(), A.end(), cmp); // 第一步 for (int i = 0; i < A.size(); i++) { // 第二步 if (A[i] < 0 && K > 0) { A[i] *= -1; K--; } } if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步 int result = 0; for (int a : A) result += a; // 第四步 return result; } };
加油站
题目链接:力扣
这题!感觉思路很乱,后面看到了leetcode上有人的题解 感觉说的很好,这里引用一下:
有一个环形路上有n个站点; 每个站点都有一个好人或一个坏人; 好人会给你钱,坏人会收你一定的过路费,如果你带的钱不够付过路费,坏人会跳起来把你砍死; 问:从哪个站点出发,能绕一圈活着回到出发点?
首先考虑一种情况:如果全部好人给你 的钱加起来 小于 坏人收的过路费之和,那么总有一次你的钱不够付过路费,你的结局注定会被砍死。
假如你随机选一点 start 出发,那么你肯定会选一个有好人的站点开始,因为开始的时候你没有钱,遇到坏人只能被砍死;
现在你在start出发,走到了某个站点end,被end站点的坏人砍死了,说明你在 [start, end) 存的钱不够付 end点坏人的过路费,因为start站点是个好人,所以在 (start, end) 里任何一点出发,你存的钱会比现在还少,还是会被end站点的坏人砍死;
于是你重新读档,聪明的选择从 end+1点出发,继续你悲壮的征程; 终于有一天,你发现自己走到了尽头(下标是n-1)的站点而没有被砍死; 此时你犹豫了一下,那我继续往前走,身上的钱够不够你继续走到出发点Start?
当然可以,因为开始已经判断过,好人给你的钱数是大于等于坏人要的过路费的,你现在攒的钱完全可以应付 [0, start) 这一段坏人向你收的过路费。 这时候你的嘴角微微上扬,眼眶微微湿润,因为你已经知道这个世界的终极奥秘:Start就是这个问题的答案。
首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。
每个加油站的剩余量rest[i]为gas[i] - cost[i]。
i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
vector<int>a(gas.size());
int sum =0;
for(int i=0;i<gas.size();i++)
{
a[i] = gas[i] -cost[i];
sum += a[i];
}
if(sum < 0) //总花销大于总收入,无论怎么走都走不出去
return -1;
int start = 0;
sum = 0;
for(int i=0; i<a.size();i++)
{
sum += a[i];
if(sum < 0)
{
sum=0;
start = i+1;
}
}
return start;
}
};
分发糖果
题目链接:力扣
本题既要和左边的小孩进行比较,又要和右边的小孩进行比较,但如果两边一起考虑,会导致顾此失彼的情况。
需要考虑两边情况的题目,一定要先确定一边 再确定另一边。
我们先考虑 右边小孩比左边小孩得分高的情况。
for(int i=1;i<ratings.size();i++) //从前遍历,右边比左边大
{
if(ratings[i] > ratings[i-1])
result[i] = result[i-1]+1;
}
再考虑左孩子比右孩子得分高的情况,此时要从后向前遍历
由于前一步已经计算了i处右边孩子比左边孩子大是的糖果数(放入result[i]中),现在的左边孩子比右边孩子大时,糖果数为result[i+1]+1,将左边孩子比较情况和右边孩子比较情况 取一个最大值
for(int i=ratings.size()-2; i>=0;i--) //从后遍历,左边比右边大
{
if(ratings[i] > ratings[i+1])
result[i] = max(result[i+1]+1,result[i]);
}
最后将result累加,可以得到答案RES。
for(int i=0;i<ratings.size();i++)
RES += result[i];