跳转至

Problem 135

# Leetcode 135 Candy

https://leetcode.cn/problems/candy/description/

大常数算法:建图,求最长路

由于每个点的入度出度不超过 2,因此求最长路是 O(n) 的。

所有入度为 0 的点都是没有限制条件的,因此可以直接分给他们 0 个糖果(不妨令所有人手里的糖果数 - 1)。

// My Solution
const int MAXN = 2e4 + 3;
struct Edge{
    int node, val;
};
struct Node {
    vector<Edge> e;
    int in;
};

class Solution {
    Node edges[MAXN];
    void addEdge(int from, int to, int val) {
        if (from == to) return;
        edges[from].e.push_back((Edge){to, val});
        edges[to].in++;
    }
    void leftside(int i, int left, int midd) {
        if (left > midd) 
            addEdge(i, i - 1, 1);
    }
    void rightside(int i, int midd, int right) {
        if (right > midd)
            addEdge(i, i + 1, 1);
    }
    int dis[MAXN];

public:
    int candy(vector<int>& ratings) {
        int n = ratings.size();
        for (int i = 0; i < n; i++)
            edges[i].in = 0;

        if (n > 1)
            rightside(0, ratings[0], ratings[1]);
        for (int i = 1; i < n - 1; i++) {
            leftside(i, ratings[i - 1], ratings[i]);
            rightside(i, ratings[i], ratings[i + 1]);
        }
        if (n > 1)
            leftside(n - 1, ratings[n - 2], ratings[n - 1]);     

        vector<int> zeroin;
        for (int i = 0; i < n; i++)
            if (edges[i].in == 0) {
                zeroin.push_back(i);
            }
        for (int i = 1; i < zeroin.size(); i++) {
            addEdge(zeroin[i], zeroin[i - 1], 0);
            addEdge(zeroin[i - 1], zeroin[i], 0);
        }
        addEdge(zeroin[0], zeroin[zeroin.size() - 1], 0);
        addEdge(zeroin[zeroin.size() - 1], zeroin[0], 0);

        std::queue<int> q;
        memset(dis, -1, sizeof(dis));
        for (int i = 0; i < zeroin.size(); i++) {
            dis[zeroin[i]] = 0; q.push(zeroin[i]);
        }
        while (!q.empty()) {
            auto node = q.front();
            q.pop();
            for (auto e : edges[node].e) {
                if (dis[e.node] < dis[node] + e.val) {
                    dis[e.node] = dis[node] + e.val;
                    q.push(e.node);
                }
            }
        }
        int sum = 0;
        for (int i = 0; i < n; i++)
            sum += dis[i];
        return sum + n;
    }
};

贪心

我们遍历该数组两次,处理出每一个学生分别满足左规则或右规则时,最少需要被分得的糖果数量。每个人最终分得的糖果数量即为这两个数量的最大值。

以左规则为例,如果递增,则糖果数 +1;如果递减,则糖果数 =1

空间优化的贪心

注意到糖果总是尽量少给,且从 1 开始累计,每次要么比相邻的同学多给一个,要么重新置为 1。

我们从左到右枚举每一个同学,记前一个同学分得的糖果数量为 pre

如果当前同学比上一个同学评分高,说明我们就在最近的递增序列中,直接分配给该同学 pre+1 个糖果即可。

否则我们就在一个递减序列中,我们直接分配给当前同学一个糖果,并把该同学所在的递减序列中所有的同学都再多分配一个糖果,以保证糖果数量还是满足条件。

我们无需显式地额外分配糖果,只需要记录当前的递减序列长度,即可知道需要额外分配的糖果数量。

递减序列一般无需包含峰值,但是要注意当当前的递减序列长度和上一个递增序列等长时,需要把最近的递增序列的最后一个同学也并进递减序列中(此时需要包含峰值)。

这样,我们只要记录当前递减序列的长度,最近的递增序列的长度,和前一个同学分得的糖果数量 pre 即可。

作者:力扣官方题解 链接:https://leetcode.cn/problems/candy/solutions/533150/fen-fa-tang-guo-by-leetcode-solution-f01p/

评论