目录
一、分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子
i
,都有一个胃口值g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j
,都有一个尺寸s[j]
。如果s[j] >= g[i]
,我们可以将这个饼干j
分配给孩子i
,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。示例 1:
输入: g = [1,2,3], s = [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。示例 2:
输入: g = [1,2], s = [1,2,3] 输出: 2 解释: 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2.
首先我们思考贪心策略,本题由于饼干不能分开(一块饼干只能喂一个人) 所以如果想让更多的人吃到饼干 我们就要让小饼干喂饱小肚子 或者(大饼干喂大肚子)两个指针分别指向饼干和胃口即可
方法一:(小饼干填小肚子)
public int findContentChildren(int[] g, int[] s) {
//s 是饼干序列 g 是胃口序列
//首先肯定是要排序的,让饼干和胃口都从小到大!
Arrays.sort(g);
Arrays.sort(s);
int p1=0;//饼干的指针
int p2=0;//胃口的指针
int res=0;//记录已分配的饼干数量
while (p1<s.length&&p2<g.length){//只要饼干和胃口有一个走完就没必要再分配饼干
if (s[p1]>=g[p2]){//小饼干大于小胃口 饼干和胃口同时往后走
p1++;
p2++;
res++;
}else {//饼干没有大于胃口 换下一个更大的饼干
p1++;
}
}
return res;
}
方法二:(大饼干填大肚子)
public int findContentChildren(int[] g, int[] s) {
//s 是饼干序列 g 是胃口序列
//首先肯定是要排序的,让饼干和胃口都从小到大!
Arrays.sort(g);
Arrays.sort(s);
int p1=s.length-1;//饼干的指针
int p2=g.length-1;//胃口的指针
int res=0;//记录已分配的饼干数量
while (p1>=0&&p2>=0){//只要饼干和胃口有一个走完就没必要再分配饼干
if (s[p1]>=g[p2]){//小饼干大于小胃口 饼干和胃口同时往前走
p1--;
p2--;
res++;
}else {//饼干没有大于胃口 换下一个小肚子
p2--;
}
}
return res;
}
哈哈 其实这道题算是贪心入门题 本质上并不难 双指针也很好理解 只要按需分配即可!
二、k次取反后最大化的数组和
给你一个整数数组
nums
和一个整数k
,按以下方法修改该数组:
- 选择某个下标
i
并将nums[i]
替换为-nums[i]
。重复这个过程恰好
k
次。可以多次选择同一个下标i
。以这种方式修改数组后,返回数组 可能的最大和 。
示例 1:
输入:nums = [4,2,3], k = 1 输出:5 解释:选择下标 1 ,nums 变为 [4,-2,3] 。示例 2:
输入:nums = [3,-1,0,2], k = 3 输出:6 解释:选择下标 (1, 2, 2) ,nums 变为 [3,1,0,2] 。示例 3:
输入:nums = [2,-3,-1,5,-4], k = 2 输出:13 解释:选择下标 (1, 4) ,nums 变为 [2,3,-1,5,4] 。
这道题题意不难理解 ,甚至贪心策略也很好想(我们每次取反都取最小的元素)然后再for循环求所有元素累计和
why 为什么 每次把最小的元素取反 总的数组和可以取最大?
我们首先把数组中的元素分为两种情况:
1、数组中仍有负数 负数中最小的负数经过排序 每次必然处于第一个下标 我们把负数取反 变为正数 是不是总的数组和就大了!
2、数组中没有负数 但我们k还没取完 所以只能忍痛割爱 把最小的正数变成负数!这样的数组和就是最大的
public int largestSumAfterKNegations(int[] nums, int k) {
while (k>0){//k次取反
Arrays.sort(nums);//每次挑选最小的元素取反
nums[0]=-nums[0];
k--;
}
int res=0;//求累加和
for (int i=0;i<nums.length;i++){
res+=nums[i];
}
return res;
}
上边的贪心策略虽然很好想,但......
k还未结束的时候 每次都要进行快排 时间复杂度达到惊人的 k*N*log^N
ok 我们换个思路来看这道题 还是刚才的两个方面来思考
1、数组中仍有负数 :
我们把负数取反他肯定跑到后边去了,我们只要用一个下标来控制 取到“第二小”的数字取反即可 无需再进行一次排序
2、数组中没有负数:
我们只能“忍痛割爱”把最小的正数取反 但取反过后这个数字必然成了数组中的最小的数(因为原来的数组已经没负数了) 如果此时k没结束 我们只需要对这个最小的数左右横条 消耗完剩下k即可
思路如下 :我们先对数组进行排序 只需要一次for循环 如果数组中仍有负数 并且k还有剩余 就把这个负数取反 然后加到结果里, 如果没有负数 我们就直接把这个遍历到的这个正数 加到结果里
如果剩余的k是偶数 nums[0] nums[0]是数组中最小的正数,进行左右横跳最后左右横条一定是正数(当前数组已经没负数) 如果剩余的k是奇数 nums【0】左右横跳一定是负数 我们就用res-2倍的nums【0】(因为我们当时加入了正数的nums[0])
上代码!
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);//先对数组进行排序
int res=0;//结果
for (int i=0;i<nums.length;i++){//for循环主要是为了实现数组和累加 顺便把负数变成正数
if (nums[i]<0&&k>0){
nums[i]=-nums[i];//负数取反
k--;//消耗了一次取反
}
res+=nums[i];
}
Arrays.sort(nums);//为了找到全是正数的数组中最小的那个最小的正数 (k已经用光的情况 也不影响)
return res-(k%2==0?0:2*nums[0]);//消耗剩下的k 返回真正的结果
}
当然 有可能数组中还有负数 我们的k用完了 但是没关系 我们只是多了一次排序 k==0 对最后返回的结果也没有影响!