Day29
1005.K次取反后最大化的数组和
力扣题目链接
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。
思路
我们可以先把数组按照绝对值从大到小进行排序
[5,-4,-3,2,-1],对这个数组,从前往后进行遍历,遇到负数就变为相反数,并把k--;循环结束条件是遍历到数组末尾或k=0
如果k=0跳出循环,那就可以直接返回改变后数组的和
如果数组遍历结束跳出循环,这时k还大于零
如果k是偶数,那其实不用管了,取反两次还是自己
如果k是奇数,就把数组最后一位的元素取反,因为它的绝对值最小,取反后让sum变小的最少
代码
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
nums = IntStream.of(nums)
.boxed()
.sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1))
.mapToInt(Integer::intValue).toArray();//把数组按绝对值大小进行排序
for (int i = 0; i < nums.length && k > 0; i++) {//循环结束条件是遍历结束或k为0了
if (nums[i] < 0) {
nums[i] = -nums[i];//把绝对值最大的负数取反
k--;//处理k
}
}
if (k % 2 == 1) nums[nums.length - 1] = -nums[nums.length - 1];//k为偶数不用管,为奇数就把绝对值最小的正数取反
return Arrays.stream(nums).sum();
}
}
134. 加油站
力扣题目链接
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
思路
暴力遍历
计算gas和cost数组的差数组,对这个arr进行遍历
如果arr[i]小于0,那直接continue,这个加油站的油跑不到下一个加油站(能跑到上一个加油站么,不需要考虑,因为可以看上一个加油站能不能跑到这个加油站)
如果大于0,那就从这里开始循环,跑一圈(取余操作),如果跑的过程中haveGas小于零,那从这个加油站开始就跑不了一圈,结束内层循环;如果发现能跑一圈,那直接返回i
最后外层循环结束,每个加油站都看完了还没有找到能跑一圈的,返回-1;
比较巧妙的解法
还是先给出rest数组
对rest数组进行遍历,计算数组前i个元素的和,并计算部分和的最小值
如果遍历结束,数组所有元素和加起来小于0,说明肯定跑不到,返回-1
如果部分和的最小值都大于零,那随便跑,返回0
剩下的情况就是要研究从哪个加油站开始跑的,我们这时倒着开始遍历,找恰好能填平最小部分和的元素位置
贪心算法
局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置。
首先需要知道,rest数组累加如果不小于0,那就一定能跑玩
对rest数组进行遍历,计算部分和,如果发现计算到i,部分和为负数,那其实从0到i这一段,不可能能作为起点,那就从i+1开始继续计算部分和,部分和从0开始重新计算
如果遍历结束,累加小于0,跑不完返回-1
否则一定能跑完,其实就是部分和大于零的初始值,用index进行记录
代码
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int[] arr = new int[cost.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = gas[i] - cost[i];//计算arr数组
}
for (int i = 0; i < cost.length; i++){
if (arr[i] < 0) continue;//小于0,跑不到下一个加油站,直接continue
int index = i;//记录i
int haveGas = arr[i];//记录这个加油站的油量
while (haveGas >= 0){//haveGas小于零了,说明跑不够一圈,进行下一次外层循环
index = (index + 1) % arr.length;//注意是循环,最后一位的下一位是第一位
haveGas += arr[index];//不断更新油量,可能多也可能少
if (index == i) return i;//如果跑了一圈了,直接返回i
}
}
return -1;//外层循环结束了都没有返回,返回-1
}
}
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int[] rest = new int[gas.length];
int sum = 0;
int min = 0;
for (int i = 0; i < rest.length; i++) {
rest[i] = gas[i] - cost[i];
sum += rest[i];//计算部分和
min = Math.min(min,sum);//计算最小部分和
}
if (sum < 0) return -1;//油量不够耗油量,肯定跑不完,这里其实是剪枝操作,这一步也可以省去,填不平就返回-1
if (min == 0) return 0;//最小部分和是0.没更新过,那随便跑
for (int i = rest.length - 1; i >= 0;i--){//倒着遍历
min += rest[i];//不断加上油量
if (min >= 0) return i;//恰好填平,返回i
}
return -1;//其实不会执行到这里,但还是要返回,因为只要sum >= 0,就一定有办法让跑完
}
}
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curSum = 0;
int totalSum = 0;
int index = 0;
for (int i = 0; i < gas.length; i++) {
curSum = curSum + gas[i] - cost[i];
totalSum = totalSum + gas[i] - cost[i];
if (curSum < 0){//部分和小于零
curSum = 0;//从0开始重新计算
index = i + 1;//index从i+1开始
}
}
if (totalSum < 0) return -1;//数组加和小于零一定跑不完,否则一定跑的完
return index;//从index开始能跑完
}
}
135. 分发糖果
力扣题目链接
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:
输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
思路
需要遍历两次,一次不好考虑
先从左向右遍历,如果右边比左边分高,那就是左边的糖果+1,其他情况都给一个糖果
然后从右向左遍历,如果左边比右边分高,那就要更新糖果,取原来的和右边糖果+1最大的(注意一定要取最大的)
最后计算数组元素的和即可
代码
class Solution {
public int candy(int[] ratings) {
int[] candyVec = new int[ratings.length];
for (int i = 0; i < ratings.length; i++){//从左向右遍历
if (i > 0 && ratings[i] > ratings[i - 1])//比左边大
candyVec[i] = candyVec[i - 1] + 1;//左边的+1
else {
candyVec[i] = 1;//其他情况都给1个糖果
}
}
for (int i = ratings.length - 1; i >= 0; i--){//从右向左遍历
if (i < ratings.length - 1 && ratings[i] > ratings[i + 1])//比右边大
candyVec[i] = Math.max(candyVec[i],candyVec[i + 1] + 1);//取原来的和右边糖果数+1更大的
}
int sum = 0;
for (int candy : candyVec){
sum += candy;
}
return sum;//返回candyVec数组元素的和
}
}