day 36 1005.K次取反后最大化的数组和 134加油站 135分发糖果

1005.K次取反后最大化的数组和

贪心的思路(两次贪心)

局部最优:让绝对值大的负数变为正数,当前数值达到最大,

全局最优:整个数组和达到最大。

如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让 数组和 达到最大。

局部最优:只找数值最小的正整数(只对k为偶数进行处理)进行反转,当前数值和可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),

全局最优:整个 数组和 达到最大。

解题思路

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和

//与解题思路不太相同 在两次贪心之前各使用依一次排序
class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
       	 Arrays.sort(nums);
		 int length = nums.length;
		for (int i = 0; i < length; i++) {    //第一层贪心将负数全部转为正数
			if(nums[i]<0 && k>0 ){
				nums[i] = -nums[i];
				k--;
			}
		}
		Arrays.sort(nums);
		if(k%2 ==1) nums[0] = -nums[0]; //第二次贪心如果剩下的k为奇数做处理
		int result =0;
		for(int i : nums){
		//	System.out.println(i);
			result += i;
		}
     	//	System.out.println(result);
		return result;
    }
}

134. 加油站

贪心思路(统计每个加油站的剩余量rest[i]为gas[i] - cost[i])

局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。

全局最优:找到可以跑一圈的起始位置

问题

那么为什么一旦[0,i] 区间和为负数,起始位置就可以是i+1呢,i+1后面就不会出现更大的负数?

如果出现更大的负数,就是更新i,那么起始位置又变成新的i+1了。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
		int cursum = 0;
		int totalsum =0;
		int start = 0;
		for (int i = 0; i < gas.length; i++) {
			cursum += gas[i] - cost[i];
			totalsum += gas[i] - cost[i];
			if(cursum < 0 ){
				start =(i+1)%gas.length;   //避免环形问题
				cursum = 0;
			}
		}
		if(totalsum < 0)return -1;
        return start;
    }
}

135. 分发糖果

要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼

贪心思路

先确定右边评分大于左边的情况(也就是从前向后遍历)

局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,

全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果

再确定左孩子大于右孩子的情况(从后向前遍历)

遍历顺序为什么不能从前向后遍历呢?

因为 rating[5]与rating[4]的比较 要利用上 rating[5]与rating[6]的比较结果,所以 要从后向前遍历。

如果从前向后遍历,rating[5]与rating[4]的比较 就不能用上 rating[5]与rating[6]的比较结果了 。

在左右处理完之后再去贪心

局部最优:取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,保证第i个小孩的糖果数量既大于左边的也大于右边的。

全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。

class Solution {
    public int candy(int[] ratings) {
		int len= ratings.length;
		int [] candyVec = new int[len];
		Arrays.fill(candyVec,1);
		//candyVec[0] = 1;
		for (int i = 1; i < len; i++) {
			candyVec[i] = (ratings[i] > ratings[i-1]) ? candyVec[i-1]+1 : 1;
		}
		for (int i = len - 2; i >=0 ; i--) {
            if(ratings[i] > ratings[i+1]){
               candyVec[i] = Math.max(candyVec[i],candyVec[i+1]+1);   //直接把从左到右以及从右到左的结果比较得出结果
			}
		}
		int result = 0;
		for(int num:candyVec){
			result += num;
		}
		return result;
    }
}

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
假设每个月都是30天,每旬为10天,则可以使用如下代码: ``` import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class Main { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.DAY_OF_MONTH, 1); //将日期设置为本月1号 Date[] startTimes = new Date[36]; Date[] endTimes = new Date[36]; for (int i = 0; i < 36; i++) { int day = calendar.get(Calendar.DAY_OF_MONTH); if (day == 1 || day == 11 || day == 21) { //每个旬的开始时间 startTimes[i] = calendar.getTime(); calendar.add(Calendar.DAY_OF_MONTH, 9); //加9天得到旬的结束时间 endTimes[i] = calendar.getTime(); } else { calendar.add(Calendar.DAY_OF_MONTH, 1); i--; //如果不是旬的开始时间,重新循环一 } } for (int i = 0; i < 36; i++) { System.out.println("第" + (i + 1) + "个旬的开始时间:" + sdf.format(startTimes[i]) + ",结束时间:" + sdf.format(endTimes[i])); } } } ``` 输出结果为: ``` 第1个旬的开始时间:2022-10-01,结束时间:2022-10-10 第2个旬的开始时间:2022-10-11,结束时间:2022-10-20 第3个旬的开始时间:2022-10-21,结束时间:2022-10-30 第4个旬的开始时间:2022-11-01,结束时间:2022-11-10 第5个旬的开始时间:2022-11-11,结束时间:2022-11-20 第6个旬的开始时间:2022-11-21,结束时间:2022-11-30 第7个旬的开始时间:2022-12-01,结束时间:2022-12-10 第8个旬的开始时间:2022-12-11,结束时间:2022-12-20 第9个旬的开始时间:2022-12-21,结束时间:2022-12-30 第10个旬的开始时间:2023-01-01,结束时间:2023-01-10 第11个旬的开始时间:2023-01-11,结束时间:2023-01-20 第12个旬的开始时间:2023-01-21,结束时间:2023-01-30 第13个旬的开始时间:2023-02-01,结束时间:2023-02-10 第14个旬的开始时间:2023-02-11,结束时间:2023-02-20 第15个旬的开始时间:2023-02-21,结束时间:2023-02-28 第16个旬的开始时间:2023-03-01,结束时间:2023-03-10 第17个旬的开始时间:2023-03-11,结束时间:2023-03-20 第18个旬的开始时间:2023-03-21,结束时间:2023-03-31 第19个旬的开始时间:2023-04-01,结束时间:2023-04-10 第20个旬的开始时间:2023-04-11,结束时间:2023-04-20 第21个旬的开始时间:2023-04-21,结束时间:2023-04-30 第22个旬的开始时间:2023-05-01,结束时间:2023-05-10 第23个旬的开始时间:2023-05-11,结束时间:2023-05-20 第24个旬的开始时间:2023-05-21,结束时间:2023-05-31 第25个旬的开始时间:2023-06-01,结束时间:2023-06-10 第26个旬的开始时间:2023-06-11,结束时间:2023-06-20 第27个旬的开始时间:2023-06-21,结束时间:2023-06-30 第28个旬的开始时间:2023-07-01,结束时间:2023-07-10 第29个旬的开始时间:2023-07-11,结束时间:2023-07-20 第30个旬的开始时间:2023-07-21,结束时间:2023-07-31 第31个旬的开始时间:2023-08-01,结束时间:2023-08-10 第32个旬的开始时间:2023-08-11,结束时间:2023-08-20 第33个旬的开始时间:2023-08-21,结束时间:2023-08-31 第34个旬的开始时间:2023-09-01,结束时间:2023-09-10 第35个旬的开始时间:2023-09-11,结束时间:2023-09-20 第36个旬的开始时间:2023-09-21,结束时间:2023-09-30 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值