LeetCode #12 双周赛题解(水 + 水 + 树形DP + 区间dp)

本文深入解析LeetCode双周赛的三道难题:力扣排行榜、数组变换及树的直径,提供详细的算法思路与代码实现,涵盖数据结构、区间动态规划等核心概念。

LeetCode #12 双周赛

5097. 力扣排行榜

题目:设计一个排行榜,满足插入、前缀和、排序。

分析:数据范围很小,用不到 logn 的数据结构,直接暴力即可。
m a p map map 存编号对应成绩,每次查询暴力排序找前 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. 树的直径

题目:求树的直径(树上最长路径)

分析:经典题目,先建树,

d p [ i ] dp[i] dp[i] 维护以 i 为根的子树的深度, d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i] = max(dp[i], dp[j]+1) dp[i]=max(dp[i],dp[j]+1)(j 为 i 的儿子)。
求出任意节点 i 子树深度之后,经过 i 的最长路径就是最深的两颗子树深度相加。

经过点 i i i 的最长路径就是最深的两颗子树深度相加。最后答案就是所有点的最大值。

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,用 d p [ s ] [ e ] dp[s][e] dp[s][e] 表示删去 ( s , e ) (s, e) (s,e) 所需要最小操作数,最终答案就是 d p [ 0 ] [ n − 1 ] dp[0][n-1] dp[0][n1]

转移方程: d p [ s ] [ e ] = m i n ( d p [ s ] [ e ] , d p [ s ] [ k ] + d p [ 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][k]+dp[k+1][e]
当区间两端点相同时,有: d p [ s ] [ e ] = m i n ( d p [ s ] [ e ] , d p [ s + 1 ] [ e − 1 ] dp[s][e] = min(dp[s][e], dp[s+1][e-1] dp[s][e]=min(dp[s][e],dp[s+1][e1],即两端点可以跟里面区间最后一次一起删。

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];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值