如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。
每个加油站的剩余量rest[i]为gas[i] - cost[i]。
i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。
AC代码:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int sumGas = 0;
int sumCast = 0;
int n = gas.size();
for (int i = 0; i < n; ++i) {
sumCast += cost[i];
sumGas += gas[i];
}
if (sumCast > sumGas)
return -1;
int diff = 0;
int ans = 0;
for (int i = 0; i < n; ++i) {
diff += gas[i] - cost[i];
if (diff < 0) {
diff = 0;
ans = i + 1;
}
}
return ans;
}
};
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int totalSum = 0;
int start = 0;
for (int i = 0; i < gas.size(); i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) { // 当前累加rest[i]和 curSum一旦小于0
start = i + 1; // 起始位置更新为i+1
curSum = 0; // curSum从0开始
}
}
if (totalSum < 0) return -1; // 说明怎么走都不可能跑一圈了
return start;
}
};
- 135. 分发糖果 先确定右边评分大于左边的情况(也就是从前向后遍历) 再确定左孩子大于右孩子的情况(从后向前遍历) AC代码:
class Solution {
public:
int candy(vector<int>& ratings) {
// 初始每个孩子至少有一个糖果
vector<int> candidates(ratings.size(), 1);
// 从左到右
for (int i = 1; i < ratings.size(); ++i) {
if (ratings[i] > ratings[i - 1]) {
candidates[i] = candidates[i - 1] + 1;
}
}
// 从右到左
for (int i = ratings.size() - 2; i >= 0; --i) {
if (ratings[i] > ratings[i + 1]) {
candidates[i] = max(candidates[i + 1] + 1, candidates[i]);
}
}
return accumulate(candidates.begin(), candidates.end(), 0);
}
};
只需要维护三种金额的数量,5,10和20。 有如下三种情况:
- 情况一:账单是5,直接收下。
- 情况二:账单是10,消耗一个5,增加一个10
- 情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5 此时大家就发现 情况一,情况二,都是固定策略,都不用我们来做分析了,而唯一不确定的其实在情况三。 而情况三逻辑也不复杂甚至感觉纯模拟就可以了,其实情况三这里是有贪心的。 账单是20的情况,为什么要优先消耗一个10和一个5呢? 因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能! 所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。
代码如下:
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int cost = 5;
vector<int> costs = {5, 10, 20};
int charge = 0;
unordered_map<int, int> moneys;
for (int bill : bills) {
moneys[bill]++;
charge = bill - cost;
while (charge > 0) {
if (charge >= 10 && moneys.find(10) != moneys.end() && moneys[10] >= 1) {
charge -= 10;
moneys[10]--;
} else if (charge >= 5 && moneys.find(5) != moneys.end() && moneys[5] >= 1) {
charge -= 5;
moneys[5]--;
} else {
return false;
}
}
}
return true;
}
};
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0, ten = 0, twenty = 0;
for (int bill : bills) {
// 情况一
if (bill == 5) five++;
// 情况二
if (bill == 10) {
if (five <= 0) return false;
ten++;
five--;
}
// 情况三
if (bill == 20) {
// 优先消耗10美元,因为5美元的找零用处更大,能多留着就多留着
if (five > 0 && ten > 0) {
five--;
ten--;
twenty++; // 其实这行代码可以删了,因为记录20已经没有意义了,不会用20来找零
} else if (five >= 3) {
five -= 3;
twenty++; // 同理,这行代码也可以删了
} else return false;
}
}
return true;
}
};
遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。 如果两个维度一起考虑一定会顾此失彼。
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(), [](const vector<int>& a, const vector<int>& b) {
if (a[0] == b[0])
return a[1] < b[1];
return a[0] > b[0];
});
vector<vector<int>> ans;
ans.reserve(people.size());
for (vector<int>& p : people) {
ans.insert(ans.begin() + p[1], p);
}
return ans;
}
};