算法训练1-day25-贪心

28 阅读3分钟
  1. 134. 加油站

如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量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;
    }
};
  1. 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);
    }
};
  1. 860. 柠檬水找零

只需要维护三种金额的数量,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;
    }
};
  1. 406. 根据身高重建队列

遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。 如果两个维度一起考虑一定会顾此失彼

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;
    }
};