「程式碼隨想錄演算法訓練營」第二十五天 | 貪心演算法 part3

云雀AC了一整天發表於2024-08-01

134. 加油站

題目連結:https://leetcode.cn/problems/gas-station/
題目難度:中等
文章講解:https://programmercarl.com/0134.加油站.html
影片講解:https://www.bilibili.com/video/BV1jA411r7WX
題目狀態:沒有思路,學習題解

思路一:全域性最優解

首先將所有路徑的加油量和耗油量加一起,看是否大於0,若小於0,表示整體的加油量小於耗油量,不可能跑完全程,直接返回-1;若大於0,看其到每一站儲存的油量最小值是否大於等於0,若是,則表示其在每一站都是可以跑完的,直接返回0(表示從0開始出發可以完成全程);若並不是這樣的,則反向加出發點,看其什麼時候的最小值不會為負數,此時就是出發點的位置。

程式碼一:

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int curSum = 0;
        int min = INT_MAX;
        for(int i = 0; i < gas.size(); ++i) {
            int rest = gas[i] - cost[i];
            curSum += rest;
            min = std::min(min, curSum);
        }
        if(curSum < 0) return -1;
        if(min >= 0) return 0;

        for(int i = gas.size() - 1; i >= 0; --i) {
            int rest = gas[i] - cost[i];
            min += rest;
            if(min >= 0) return i;
        }
        return -1;
    }
};

思路二:貪心演算法

區域性最優,首先當所有路徑的油差之和小於0,直接返回-1,其不可能跑完全程;再次,若從A點到B點的油差和小於0,要從B點的下一點重新記錄,因為A到B之間無論從哪一點開始,都跑不完,最後返回合適的位置。

程式碼:

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 < cost.size(); ++i) {
            curSum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if(curSum < 0) {
                start = i + 1;
                curSum = 0;
            }
        }
        if(totalSum < 0) return -1;
        return start;
    }
};

135. 分發糖果

題目連結:https://leetcode.cn/problems/candy/
題目難度:困難
文章講解:https://programmercarl.com/0135.分發糖果.html
影片講解:https://www.bilibili.com/video/BV1ev4y1r7wN
題目狀態:好難,要長腦子了

思路:

首先建立一個陣列,儲存每個孩子手裡的糖。

  • 首先,每個孩子手裡都要有一顆糖。
  • 從左向右,判斷右邊孩子分數是否要大於左邊孩子,若大於,就把右邊孩子手裡加一顆糖。
  • 從右向左,判斷左邊孩子的分數是否大於右邊孩子,若大於,則判斷左邊孩子手裡的糖是否已經大於右邊孩子了,若不大於,則將左邊孩子手裡的糖加到比右邊孩子大。

程式碼:

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int> candyVal(ratings.size(), 1);
        for(int i = 1; i < ratings.size(); ++i) {
            if(ratings[i] > ratings[i - 1]) candyVal[i] = candyVal[i - 1] + 1;
        }
        for(int i = ratings.size() - 2; i >= 0; --i) {
            if(ratings[i] > ratings[i + 1]) candyVal[i] = max(candyVal[i], candyVal[i + 1] + 1);
        }
        int res = 0;
        for(auto &val : candyVal) res += val;
        return res;
    }
};

860. 檸檬水找零

題目連結:https://leetcode.cn/problems/lemonade-change/
題目難度:簡單
文章講解:https://programmercarl.com/0860.檸檬水找零.html
影片講解:https://www.bilibili.com/video/BV12x4y1j7DD
題目狀態:沒繞過來,看完題解豁然開朗

思路:

維護五塊錢和十塊錢的個數five和ten

  • 當給5元時,five++;
  • 當給10元時,five--;
  • 當給20元時,要麼ten--和five--,要麼five-3。

程式碼:

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int five = 0, ten = 0;
        for(int i = 0; i < bills.size(); ++i) {
            if(bills[i] == 5) five++;
            if(bills[i] == 10) {
                if(five <= 0) return false;
                five--;
                ten++;
            }
            if(bills[i] == 20) {
                if(five > 0 && ten > 0) {
                    five--;
                    ten--;
                } else if(five >= 3) {
                    five -= 3;
                } else {
                    return false;
                }
            }
        }
        return true;
    }
};

406. 根據身高重建佇列

題目連結:https://leetcode.cn/problems/queue-reconstruction-by-height/
題目難度:中等
文章講解:https://programmercarl.com/0406.根據身高重建佇列.html
影片講解:https://www.bilibili.com/video/BV1EA411675Y
題目狀態:繼續學習題解...

思路:

首先對整個陣列進行排序,排序規則是比較身高,最高的排在前面,若身高相同,比較前方人數,人數低的排在前面。
之後開始插入返回陣列,按照排序後的陣列依次插入,插入的位置是自己的前方人數,下圖更容易理解插入的過程。

程式碼實現:

class Solution {
public:
    static bool cmp(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>> reconstructQueue(vector<vector<int>>& people) {
        sort(people.begin(), people.end(), cmp);
        vector<vector<int>> que;
        for(int i = 0; i < people.size(); ++i) {
            int position = people[i][1];
            que.insert(que.begin() + position, people[i]);
        }
        return que;
    }
};

連結串列程式碼:(效能更好)

class Solution {
public:
    // 身高從大到小排(身高相同k小的站前面)
    static bool cmp(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>> reconstructQueue(vector<vector<int>>& people) {
        sort (people.begin(), people.end(), cmp);
        list<vector<int>> que; // list底層是連結串列實現,插入效率比vector高的多
        for (int i = 0; i < people.size(); i++) {
            int position = people[i][1]; // 插入到下標為position的位置
            std::list<vector<int>>::iterator it = que.begin();
            while (position--) { // 尋找在插入位置
                it++;
            }
            que.insert(it, people[i]);
        }
        return vector<vector<int>>(que.begin(), que.end());
    }
};

相關文章