LeetCode #12 雙週賽題解(水 + 水 + 樹形DP + 區間dp)

LeetCode #12 雙週賽

5097. 力扣排行榜

題目:設計一個排行榜,滿足插入、前綴和、排序。

分析:數據範圍很小,用不到 logn 的數據結構,直接暴力即可。
mapmap 存編號對應成績,每次查詢暴力排序找前 k 和。

class Leaderboard {
public:
    map<int, int> mp;
    Leaderboard() {}
     
    void addScore(int playerId, int score) {
        mp[playerId] += score;
    }

    int top(int K) {
        vector<int> arr;
        for (auto it = mp.begin(); it != mp.end(); it++) {
            arr.push_back(it->second);
        }
        sort(arr.begin(), arr.end());
        int ans = 0, pos = arr.size() - 1;
        while (K--) {
            ans += arr[pos--];
        }
        return ans;
    }

    void reset(int playerId) {
        mp[playerId] = 0;
    }
};

5096. 數組變換

題目:

分析:按題意模擬

class Solution {
public:
    vector<int> transformArray(vector<int>& arr) {
        int n = arr.size(), flag = 1, p = 1;
        vector<int> ans[2];
        for (auto x : arr) {
            ans[0].push_back(x); ans[1].push_back(x);
        }
        while (flag) {
            flag = 0;
            for (int i = 0; i < n; i++) {
                if (i == 0 || i == n - 1) {
                    ans[p][i] = ans[p ^ 1][i];
                } else if (ans[p ^ 1][i] > ans[p ^ 1][i - 1] && ans[p ^ 1][i] > ans[p ^ 1][i + 1]) {
                    ans[p][i] = ans[p ^ 1][i] - 1; flag = 1; 
                } else if (ans[p ^ 1][i] < ans[p ^ 1][i - 1] && ans[p ^ 1][i] < ans[p ^ 1][i + 1]) {
                    ans[p][i] = ans[p ^ 1][i] + 1; flag = 1; 
                } else {
                    ans[p][i] = ans[p ^ 1][i];
                }
            }
            p ^= 1;
        }
        return ans[p ^ 1];
    }
};

5098. 樹的直徑

題目:求樹的直徑(樹上最長路徑)

分析:經典題目,先建樹,

dp[i]dp[i] 維護以 i 爲根的子樹的深度,dp[i]=max(dp[i],dp[j]+1)dp[i] = max(dp[i], dp[j]+1)(j 爲 i 的兒子)。
求出任意節點 i 子樹深度之後,經過 i 的最長路徑就是最深的兩顆子樹深度相加。

經過點 ii 的最長路徑就是最深的兩顆子樹深度相加。最後答案就是所有點的最大值。

class Solution {
public:
    void DP(int* dp, int rt, vector<int>* son, int& ans) {
        dp[rt] = 0;
        int m1 = 0, m2 = 0;
        for (auto x : son[rt]) {
            DP(dp, x, son, ans);
            if(dp[x] + 1 > m1){
                m2 = m1;
                m1 = dp[x] + 1;
            }else if(dp[x] + 1 > m2){
                m2 = dp[x] + 1;
            }
        }
        dp[rt] = m1;
        ans = max(ans, m1 + m2);
    }
    
    int treeDiameter(vector<vector<int>>& edges) {
        vector<int> son[10010];
        int dp[10010];
        int n = 0, vis[10010], rt, ans = 0;
        for (auto x : edges) {
            son[x[0]].push_back(x[1]);
            n = max(n, max(x[0], x[1]));
            vis[x[1]]++;
        }
        for (int i = 0; i <= n; i++) {
            if (!vis[i]) {
                rt = i; break;
            }
        }
        DP(dp, rt, son, ans);
        return ans;
    }
};

5115. 刪除迴文子數組

題目:給一個數組,每次可以刪去迴文子數組,刪去之後右邊的右移。問刪去整個數組所需要的最小操作次數。

分析:明顯區間 dp,用 dp[s][e]dp[s][e] 表示刪去 (s,e)(s, e) 所需要最小操作數,最終答案就是 dp[0][n1]dp[0][n-1]

轉移方程:dp[s][e]=min(dp[s][e],dp[s][k]+dp[k+1][e]dp[s][e] = min(dp[s][e], dp[s][k] + dp[k+1][e]
當區間兩端點相同時,有:dp[s][e]=min(dp[s][e],dp[s+1][e1]dp[s][e] = min(dp[s][e], dp[s+1][e-1],即兩端點可以跟裏面區間最後一次一起刪。

class Solution {
    const int N = 1e2 + 10;
    const int INF = 0x3f3f3f;
public:
    int minimumMoves(vector<int>& v) {
        int n = v.size();
        int dp[N][N];
        memset(dp, INF, sizeof(dp));
        for (int i = 0; i < n; i++) {
            dp[i][i] = 1;
        }
        for (int l = 2; l <= n; l++) {     // 枚舉區間長度, 長度爲 1 的已經初始化了
            for (int s = 0; s + l - 1 < n; s++) {     // 枚舉區間起點 s
                int e = s + l - 1;
                for (int k = s; k <= e; k++) {   // 找中點位置
                    dp[s][e] = min(dp[s][e], dp[s][k] + dp[k + 1][e]);
                }
                if (v[s] == v[e]) {
                    if (l == 2) dp[s][e] = 1;
                    else dp[s][e] = min(dp[s][e], dp[s + 1][e - 1]);
                }
            }
        }
        return dp[0][n - 1];
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章