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

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

56. 合併區間

題目連結:https://leetcode.cn/problems/merge-intervals/
題目難度:中等
文章講解:https://programmercarl.com/0056.合併區間.html
影片講解:https://www.bilibili.com/video/BV1wx4y157nD
題目狀態:有思路,嘻嘻🤣

思路:

首先,按照每個區間開頭的前後順序進行排列,然後定義一個vector<int>型別的變數來儲存我們合併的區間,迴圈遍歷每個區間,判斷當前合併的區間結尾是否大於等於下一個區間的開頭。若大於,表示可以將該區間合併起來,否則,表明下面的區間不會和已經合併的區間有重疊了,將其存入返回變數,並改變其為下一個區間,繼續遍歷,直到遍歷完。

程式碼:

點選檢視程式碼
class Solution {
public:
    static bool cmp(const vector<int> &a, const vector<int> &b) {
        return a[0] < b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
        vector<vector<int>> res;
        vector<int> temp = intervals[0];
        for(int i = 1; i < intervals.size(); ++i) {
            if(temp[1] >= intervals[i][0]) {
                temp[1] = max(temp[1], intervals[i][1]);
            } else {
                res.push_back(temp);
                temp = intervals[i];
            }
        }
        res.push_back(temp);
        return res;
    }
};

738. 單調遞增的數字

題目連結:https://leetcode.cn/problems/monotone-increasing-digits/
題目難度:中等
文章講解:https://programmercarl.com/0738.單調遞增的數字.html
影片講解:https://www.bilibili.com/video/BV1Kv4y1x7tP
題目狀態:沒有思路,不嘻嘻😭

思路:

從後往前遍歷(這是關鍵),判斷前一位數是否大於後一位數:

  • 若大於,將前一位數減1,這表明前一位數已經大於後面的數了,只能將前面的數減1,之後在將後面的數置為9
  • 若不大於,則向前繼續遍歷。

注意要記錄最後一個減1的位數的下一個位數,之後要統一將其後面的數置為9.

程式碼:

點選檢視程式碼
class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string strNum = to_string(n);
        int flag = strNum.size();
        for(int i = strNum.size() - 1; i > 0; --i) {
            if(strNum[i - 1] > strNum[i]) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for(int i = flag; i < strNum.size(); ++i) strNum[i] = '9';
        return stoi(strNum);
    }
};

968. 監控二叉樹

題目連結:hhttps://leetcode.cn/problems/binary-tree-cameras/
題目難度:困難
文章講解:https://programmercarl.com/0968.監控二叉樹.html
影片講解:https://www.bilibili.com/video/BV1SA411U75i
題目狀態:看完題目就知道了,要看題解了😭

思路:

首先,明確遍歷順序,要用後序遍歷來實現從下往上遍歷,因為要避免葉子節點安裝監控(這種情況下會產生最大的浪費)。

之後,將節點的狀態分為以下幾種:

  1. 本節點沒有被攝像頭覆蓋,用0表示;
  2. 本節點上安裝攝像頭,用1表示;
  3. 本節點被攝像頭覆蓋,用2表示。

注意:遞迴結果是判斷該節點是否為nullptr來結束的,通常節點為nullptr時說明我們遍歷到了葉子節點的下面,那我們要給空節點設定一個狀態,為了符合下面判斷的邏輯,我們要將空節點設定為狀態2(儘管並沒有被覆蓋),因為若我們將空節點設定為沒有被覆蓋的狀態,我們就需要將葉子節點設定為狀態1,此時就出現了最大浪費。

下面我們進行單層的邏輯:

  1. 本節點的左右孩子都有被覆蓋,即left == 2 && right == 2
    此時該節點沒有被覆蓋,因此該節點的狀態為0
  2. 本節點的左右孩子中至少有一個是沒有被覆蓋的,即left == 0 || right == 0
    此時該節點需要對下面的孩子進行覆蓋,因此需要將攝像頭的個數加一,然後返回狀態1
  3. 本節點的左右孩子中至少一個安裝了攝像頭,即left == 1 || right == 1
    此時該節點處於被覆蓋狀態,返回狀態2

注意:當遍歷完之後,若發現返回的根結點的狀態是0,攝像頭需要加一。

程式碼:

點選檢視程式碼
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int res;
    int traversal(TreeNode *cur) {
        if(cur == nullptr) return 2;
        int left = traversal(cur->left);
        int right = traversal(cur->right);
        if(left == 2 && right == 2) {
            return 0;
        }
        if(left == 0 || right == 0) {
            res++;
            return 1;
        }
        if(left == 1 || right == 1) {
            return 2;
        }
        return -1;
    }
    int minCameraCover(TreeNode* root) {
        res = 0;
        if(traversal(root) == 0) {
            res++;
        }
        return res;
    }
};

相關文章