2019LeetCode第 12 场双周赛

5097. 力扣排行榜

题目描述

新一轮的「力扣杯」编程大赛即将启动,为了动态显示参赛者的得分数据,需要设计一个排行榜 Leaderboard。

请你帮忙来设计这个 Leaderboard 类,使得它有如下 3 个函数:

addScore(playerId, score):
假如参赛者已经在排行榜上,就给他的当前得分增加 score 点分值并更新排行。
假如该参赛者不在排行榜上,就把他添加到榜单上,并且将分数设置为 score。
top(K):返回前 K 名参赛者的 得分总和。
reset(playerId):将指定参赛者的成绩清零。题目保证在调用此函数前,该参赛者已有成绩,并且在榜单上。
请注意,在初始状态下,排行榜是空的。

示例 1:
输入:
[“Leaderboard”,“addScore”,“addScore”,“addScore”,“addScore”,“addScore”,“top”,“reset”,“reset”,“addScore”,“top”]
[[],[1,73],[2,56],[3,39],[4,51],[5,4],[1],[1],[2],[2,51],[3]]
输出:
[null,null,null,null,null,null,73,null,null,null,141]

解释:
Leaderboard leaderboard = new Leaderboard ();
leaderboard.addScore(1,73); // leaderboard = [[1,73]];
leaderboard.addScore(2,56); // leaderboard = [[1,73],[2,56]];
leaderboard.addScore(3,39); // leaderboard = [[1,73],[2,56],[3,39]];
leaderboard.addScore(4,51); // leaderboard = [[1,73],[2,56],[3,39],[4,51]];
leaderboard.addScore(5,4); // leaderboard = [[1,73],[2,56],[3,39],[4,51],[5,4]];
leaderboard.top(1); // returns 73;
leaderboard.reset(1); // leaderboard = [[2,56],[3,39],[4,51],[5,4]];
leaderboard.reset(2); // leaderboard = [[3,39],[4,51],[5,4]];
leaderboard.addScore(2,51); // leaderboard = [[2,51],[3,39],[4,51],[5,4]];
leaderboard.top(3); // returns 141 = 51 + 51 + 39;

提示:
1 <= playerId, K <= 10000
题目保证 K 小于或等于当前参赛者的数量
1 <= score <= 100
最多进行 1000 次函数调用

题解1

这里最多进行1000次函数调用,最多有1000名参赛者,可以用vector暴力求解。playerId对应数组下标,addScore和reset都是根据id修改值,top是将vector复本排序,不要直接对vector排序,不然下标乱了,然后输出前3。
我这里采用的不是这个思路,用到set来存储score和id,set会自动根据score排序。然后用一个map来存储set中每个元素的位置,这里的思路采用的是ccf中一道题的思路。具体见代码

代码1

struct Node{//定义结构体
    int playerId;
    int score;
    Node(int playerId = 0, int score = 0):playerId(playerId), score(score){}
    bool operator < (const Node &a)const{
        return score >= a.score;//如果写成score > a.score 相同的将不会插入
    }
};

class Leaderboard {
public:
    set<Node>board;
    map<int, set<Node>::iterator>m;
    Leaderboard() {
        
    }
    void addScore(int playerId, int score) {//添加成绩
        if(m.find(playerId) != m.end()){
            int tmpScore = m[playerId]->score;
            board.erase(m[playerId]);
            m.erase(playerId);
            m[playerId] = board.insert(Node(playerId, tmpScore + score)).first;//map存储插之后的位置
        }
        else{
            m[playerId] = board.insert(Node(playerId, score)).first;
        }
    }
    
    int top(int K) {//获取前k名成绩总和
        int sum = 0, cnt = 0;
        for(auto iter = board.begin(); iter != board.end(); ++iter){
            sum += iter->score;
            ++cnt;
            if(cnt == K){
                break;
            }
        }
        return sum;
    }
    
    void reset(int playerId) {//重置成绩
        board.erase(m[playerId]);
        m.erase(playerId);
    }
};

/**
 * Your Leaderboard object will be instantiated and called as such:
 * Leaderboard* obj = new Leaderboard();
 * obj->addScore(playerId,score);
 * int param_2 = obj->top(K);
 * obj->reset(playerId);
 */
 

5096. 数组变换

题目描述

首先,给你一个初始数组 arr。然后,每天你都要根据前一天的数组生成一个新的数组。
第 i 天所生成的数组,是由你对第 i-1 天的数组进行如下操作所得的:
假如一个元素小于它的左右邻居,那么该元素自增 1。
假如一个元素大于它的左右邻居,那么该元素自减 1。
首、尾元素 永不 改变。
过些时日,你会发现数组将会不再发生变化,请返回最终所得到的数组。

示例 1:
输入:[6,2,3,4]
输出:[6,3,3,4]
解释:
第一天,数组从 [6,2,3,4] 变为 [6,3,3,4]。
无法再对该数组进行更多操作。

示例 2:
输入:[1,6,3,4,3,5]
输出:[1,4,4,4,4,5]
解释:
第一天,数组从 [1,6,3,4,3,5] 变为 [1,5,4,3,4,5]。
第二天,数组从 [1,5,4,3,4,5] 变为 [1,4,4,4,4,5]。
无法再对该数组进行更多操作。

提示:
1 <= arr.length <= 100
1 <= arr[i] <= 100

题解1

暴力求解,注意的是第i天的数组元素都是在第i - 1天的基础上生成的,即不能直接修改,会覆盖掉,要在副本上修改。再覆盖过去。

代码1

class Solution {
public:
    vector<int> transformArray(vector<int>& arr) {
        int len = arr.size();
        if(len == 1 || len == 2){
            return arr;
        }
        vector<int>tmp(arr.begin(), arr.end());
        int res = -1;
        bool flag = false;
        while(true){
            flag = true;//flag用来标记此次是否有修改,没有的话表示数据稳定,可以结束循环了
            for(int i = 1; i < len - 1; ++i){
                if(arr[i] > arr[i - 1] && arr[i] > arr[i + 1]){
                    tmp[i] -= 1;
                     flag = false;
                else if(arr[i] < arr[i - 1] && arr[i] < arr[i + 1]){
                    tmp[i] += 1;
                    flag = false;
                    
                }
            }
            arr.assign(tmp.begin(), tmp.end());
            if(flag){
                break;
            }
            
        }
        return tmp;
    }
};

5098. 树的直径

题目描述

给你这棵「无向树」,请你测算并返回它的「直径」:这棵树上最长简单路径的 边数。
我们用一个由所有「边」组成的数组 edges 来表示一棵无向树,其中 edges[i] = [u, v] 表示节点 u 和 v 之间的双向边。
树上的节点都已经用 {0, 1, …, edges.length} 中的数做了标记,每个节点上的标记都是独一无二的。

示例 1:
2019LeetCode第 12 场双周赛_第1张图片
输入:edges = [[0,1],[0,2]]
输出:2
解释:
这棵树上最长的路径是 1 - 0 - 2,边数为 2。

示例 2:
2019LeetCode第 12 场双周赛_第2张图片
输入:edges = [[0,1],[1,2],[2,3],[1,4],[4,5]]
输出:4
解释:
这棵树上最长的路径是 3 - 2 - 1 - 4 - 5,边数为 4。

提示:
0 <= edges.length < 10^4
edges[i][0] != edges[i][1]
0 <= edges[i][j] <= edges.length
edges 会形成一棵无向树

题解1

初始想法是用BFS,类似于求树的最大深度,求出从结点0出发几棵子树的深度,然后深度较大的两个组成的路径就是最大路径。代码写完了发现第二个样例就过不了,思路有问题。初始节点的选取必须要是最长路径的路子节点,而结点0并不一定能满足要求。
参见圈子的讨论:思路是两次BFS,第一次是求取最长路径的端点,然后从这个节点出发BFS,求得的最长路径即为所求。

代码1

vector<vector<int> >tree(10005);
int dist[10005];
bool mark[10005];
class Solution {
public:
    pair<int, int> bfs(int start){//bfs最大深度的所在叶子节点及最大深度
        memset(dist, 0, sizeof(dist));//重置
        memset(mark, false, sizeof(mark));
        queue<int>que;
        que.push(start);
        mark[start] = true;
        int maxDis = 0, res = 0;
        int frt;
        while(!que.empty()){
            frt = que.front();
            que.pop();
            if(frt == start){
                for(int i = 0; i < tree[frt].size(); ++i){
                    dist[tree[frt][i]] = 1;
                    que.push(tree[frt][i]);
                    mark[tree[frt][i]] = true;
                }
            }
            else{
                bool flag = true;
                for(int i = 0; i < tree[frt].size(); ++i){
                    if(!mark[tree[frt][i]]){
                        dist[tree[frt][i]] = dist[frt] + 1;
                        que.push(tree[frt][i]);
                        mark[tree[frt][i]] = true;
                        flag = false;
                    }
                }
                if(flag && dist[frt] > maxDis){
                    res = frt;
                    maxDis = dist[frt];
                }
            }
        }
        return make_pair(res, maxDis);
    }
    int treeDiameter(vector<vector<int>>& edges) {
        int len = edges.size(), start;
        for(int i = 0; i < len; ++i){//生成图(树)
            tree[edges[i][0]].push_back(edges[i][1]);
            tree[edges[i][1]].push_back(edges[i][0]);
        }
        int newSta = bfs(0).first;//第一次bfs
        cout << newSta << endl;
        return bfs(newSta).second;//第二次bfs
    }
};
//代码本地执行结果与提交执行结果不一致,很奇怪

5115. 删除回文子数组

题目描述

给你一个整数数组 arr,每一次操作你都可以选择并删除它的一个 回文 子数组 arr[i], arr[i+1], …, arr[j]( i <= j)。
注意,每当你删除掉一个子数组,右侧元素都会自行向前移动填补空位。
请你计算并返回从数组中删除所有数字所需的最少操作次数。

示例 1:
输入:arr = [1,2]
输出:2

示例 2:
输入:arr = [1,3,4,1,5]
输出:3
解释:先删除 [4],然后删除 [1,3,1],最后再删除 [5]。

提示:
1 <= arr.length <= 100
1 <= arr[i] <= 20

题解1

有时间再写吧,没做出来!

代码1

你可能感兴趣的:(LeetCode)