134. 加油站
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int n = gas.length;
int[] need = new int[n];
for(int i=0; i<n; i++){
need[i] = cost[i] - gas[i];
}
int start=0;
while(start<n){
int temp = 0;
int i=start;
for(; i<(start+n); i++){
temp+=need[i%n];
if(temp>0) break;
}
if(temp>0) start=i+1;
else break;
}
if(start<n) return start;
else return -1;
}
}
135. 分发糖果
如随想录中所说,想一次性将所有条件全考虑完,会导致顾此失彼。
此时,一定要确定一边之后,再确定另一边。例如比较每一个孩子的左边,然后再比较右边。
我原本是想,i 一个个遍历,与左边相比较:但是真不好更新,很复杂
- 比左边大:左边+1
- 和左边一样大:设为1
- 比左边小:左边是否大于1——如果大于1,就设为1;如果等于1,就要更新前面所有值。
// 失败解法
class Solution {
public int candy(int[] ratings) {
int[] can = new int[ratings.length];
Arrays.fill(can, 1);
// 两边相等:取更小的
// 左大右小:比小的+1即可
for(int i=1; i<n; i++){ // 一旦不平衡,就更新
int cur=i;
if(ratings[cur] > ratings[cur-1]) can[cur] = can[cur-1]+1;
else if(ratings[cur] == ratings[cur-1]){
}else if (ratings[cur] < ratings[cur-1]){ // 更新前面
if(can[cur] < can[cur-1])
// 写不下去了……
}
}
return
}
}
随后的解法:时间复杂度比较高
class Solution {
public int candy(int[] ratings) {
int[] can = new int[ratings.length];
Arrays.fill(can, 1);
// 从左向右遍历
for(int i=1; i<ratings.length; i++){
if(ratings[i] > ratings[i-1]) can[i] = Math.max(can[i], can[i-1]+1);
}
// 从右向左遍历
for(int i=ratings.length-2; i>=0; i--){
if(ratings[i] > ratings[i+1]) can[i] = Math.max(can[i], can[i+1]+1);
}
return Arrays.stream(can).sum();
}
}
代码随想录解法:
class Solution {
/**
分两个阶段
1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1
2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,此时 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值,这样才符合 它比它左边的大,也比它右边大
*/
public int candy(int[] ratings) {
int len = ratings.length;
int[] candyVec = new int[len];
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 ans = 0;
for (int num : candyVec) {
ans += num;
}
return ans;
}
}
860.柠檬水找零
局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。
class Solution {
public boolean lemonadeChange(int[] bills) {
int[] arr = new int[2];
int temp = 0;
for(int b:bills){
switch (b){
case 5:
arr[0]++;
break;
case 10:
arr[1]++;
temp = --arr[0];
break;
case 20:
if(arr[1]>0){
arr[1]--;
temp = --arr[0];
}else{
arr[0] -= 3;
temp = arr[0];
}
break;
}
if(temp<0) return false;
}
return true;
}
}
406.根据身高重建队列
类似于分发糖果:遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。
所以在按照身高从大到小排序后:
局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性
全局最优:最后都做完插入操作,整个队列满足题目队列属性
整个插入过程如下:按照身高顺序,依次按照对应下表擦汗如。
排序完的people: [[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
插入的过程:
插入[7,0]:[[7,0]]
插入[7,1]:[[7,0],[7,1]]
插入[6,1]:[[7,0],[6,1],[7,1]]
插入[5,0]:[[5,0],[7,0],[6,1],[7,1]]
插入[5,2]:[[5,0],[7,0],[5,2],[6,1],[7,1]]
插入[4,4]:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
class Solution {
public int[][] reconstructQueue(int[][] people) {
// 先按照身高排,再按照……
Arrays.sort(people, (a, b) -> { // 身高从小到大
if(a[0]!=b[0]) return b[0]-a[0];
else return a[1]-b[1];
});
LinkedList<int[]> que = new LinkedList<>();
for (int[] p : people) {
que.add(p[1],p); // 插入到指定位置
}
return que.toArray(new int[people.length][]);
}
}