普通视图

发现新文章,点击刷新页面。
今天 — 2025年12月27日LeetCode 每日一题题解

每日一题-会议室 III🔴

2025年12月27日 00:00

给你一个整数 n ,共有编号从 0n - 1n 个会议室。

给你一个二维整数数组 meetings ,其中 meetings[i] = [starti, endi] 表示一场会议将会在 半闭 时间区间 [starti, endi) 举办。所有 starti 的值 互不相同

会议将会按以下方式分配给会议室:

  1. 每场会议都会在未占用且编号 最小 的会议室举办。
  2. 如果没有可用的会议室,会议将会延期,直到存在空闲的会议室。延期会议的持续时间和原会议持续时间 相同
  3. 当会议室处于未占用状态时,将会优先提供给原 开始 时间更早的会议。

返回举办最多次会议的房间 编号 。如果存在多个房间满足此条件,则返回编号 最小 的房间。

半闭区间 [a, b)ab 之间的区间,包括 a 不包括 b

 

示例 1:

输入:n = 2, meetings = [[0,10],[1,5],[2,7],[3,4]]
输出:0
解释:
- 在时间 0 ,两个会议室都未占用,第一场会议在会议室 0 举办。
- 在时间 1 ,只有会议室 1 未占用,第二场会议在会议室 1 举办。
- 在时间 2 ,两个会议室都被占用,第三场会议延期举办。
- 在时间 3 ,两个会议室都被占用,第四场会议延期举办。
- 在时间 5 ,会议室 1 的会议结束。第三场会议在会议室 1 举办,时间周期为 [5,10) 。
- 在时间 10 ,两个会议室的会议都结束。第四场会议在会议室 0 举办,时间周期为 [10,11) 。
会议室 0 和会议室 1 都举办了 2 场会议,所以返回 0 。 

示例 2:

输入:n = 3, meetings = [[1,20],[2,10],[3,5],[4,9],[6,8]]
输出:1
解释:
- 在时间 1 ,所有三个会议室都未占用,第一场会议在会议室 0 举办。
- 在时间 2 ,会议室 1 和 2 未占用,第二场会议在会议室 1 举办。
- 在时间 3 ,只有会议室 2 未占用,第三场会议在会议室 2 举办。
- 在时间 4 ,所有三个会议室都被占用,第四场会议延期举办。 
- 在时间 5 ,会议室 2 的会议结束。第四场会议在会议室 2 举办,时间周期为 [5,10) 。
- 在时间 6 ,所有三个会议室都被占用,第五场会议延期举办。 
- 在时间 10 ,会议室 1 和 2 的会议结束。第五场会议在会议室 1 举办,时间周期为 [10,12) 。 
会议室 1 和会议室 2 都举办了 2 场会议,所以返回 1 。 

 

提示:

  • 1 <= n <= 100
  • 1 <= meetings.length <= 105
  • meetings[i].length == 2
  • 0 <= starti < endi <= 5 * 105
  • starti 的所有值 互不相同

n<=100,代码可以写得简单(比赛时想复杂了)

作者 newhar
2022年9月4日 12:21

由于 $n\le 100$,可以直接按照题目模拟:

  1. 按开始时间排序,依次安排 meetings

  2. 维护每个会议室的 最早可用时间 $t$。每次安排会议$[start, end)$时,将那些 $t$ 早于 $start$ 的会议室的 $t$ 设为 $start$。这样处理后只需从中选择 $t$ 最早的会议室即可(如果有相等的选下标最小的)。

  3. 同时维护 $cnt$ 数组,遍历完成后按要求返回答案即可

###python

class Solution:
    def mostBooked(self, n: int, meetings: List[List[int]]) -> int:
        cnt, t = [0] * n, [0] * n
        
        for [s, e] in sorted(meetings):
            t = list(map(lambda x : max(x, s), t))
            choice = t.index(min(t))
            t[choice], cnt[choice] = t[choice] + e - s, cnt[choice] + 1
            
        return cnt.index(max(cnt))

双堆模拟(Python/Java/C++/Go)

作者 endlesscheng
2022年9月4日 12:07

本题 视频讲解 已出炉,欢迎素质三连,在评论区分享你对这场周赛的看法~


用两个小顶堆模拟:

  • $\textit{idle}$ 维护在 $\textit{start}_i$ 时刻空闲的会议室的编号;
  • $\textit{using}$ 维护在 $\textit{start}_i$ 时刻使用中的会议室的结束时间和编号。

这两类会议室是互补关系,伴随着会议的开始和结束,会议室在这两类中来回倒。

对 $\textit{meetings}$ 按照开始时间排序,然后遍历 $\textit{meetings}$,按照题目要求模拟即可,具体模拟方式见代码。

复杂度分析

  • 时间复杂度:$O(n+m(\log n + \log m))$,其中 $m$ 为 $\textit{meetings}$ 的长度。注意每个会议至多入堆出堆各一次。
  • 空间复杂度:$O(n)$。

相似题目

class Solution:
    def mostBooked(self, n: int, meetings: List[List[int]]) -> int:
        cnt = [0] * n
        idle, using = list(range(n)), []
        meetings.sort(key=lambda m: m[0])
        for st, end in meetings:
            while using and using[0][0] <= st:
                heappush(idle, heappop(using)[1])  # 维护在 st 时刻空闲的会议室
            if len(idle) == 0:
                e, i = heappop(using)  # 没有可用的会议室,那么弹出一个最早结束的会议室(若有多个同时结束的,会弹出下标最小的)
                end += e - st  # 更新当前会议的结束时间
            else:
                i = heappop(idle)
            cnt[i] += 1
            heappush(using, (end, i))  # 使用一个会议室
        ans = 0
        for i, c in enumerate(cnt):
            if c > cnt[ans]:
                ans = i
        return ans
class Solution {
    public int mostBooked(int n, int[][] meetings) {
        var cnt = new int[n];
        var idle = new PriorityQueue<Integer>();
        for (var i = 0; i < n; ++i) idle.offer(i);
        var using = new PriorityQueue<Pair<Long, Integer>>((a, b) -> !Objects.equals(a.getKey(), b.getKey()) ? Long.compare(a.getKey(), b.getKey()) : Integer.compare(a.getValue(), b.getValue()));
        Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0]));
        for (var m : meetings) {
            long st = m[0], end = m[1];
            while (!using.isEmpty() && using.peek().getKey() <= st) {
                idle.offer(using.poll().getValue()); // 维护在 st 时刻空闲的会议室
            }
            int id;
            if (idle.isEmpty()) {
                var p = using.poll(); // 没有可用的会议室,那么弹出一个最早结束的会议室(若有多个同时结束的,会弹出下标最小的)
                end += p.getKey() - st; // 更新当前会议的结束时间
                id = p.getValue();
            } else id = idle.poll();
            ++cnt[id];
            using.offer(new Pair<>(end, id)); // 使用一个会议室
        }
        var ans = 0;
        for (var i = 0; i < n; ++i) if (cnt[i] > cnt[ans]) ans = i;
        return ans;
    }
}
class Solution {
public:
    int mostBooked(int n, vector<vector<int>> &meetings) {
        int cnt[n]; memset(cnt, 0, sizeof(cnt));
        priority_queue<int, vector<int>, greater<>> idle;
        for (int i = 0; i < n; ++i) idle.push(i);
        priority_queue<pair<long, int>, vector<pair<long, int>>, greater<>> using_;
        sort(meetings.begin(), meetings.end(), [](auto &a, auto &b) { return a[0] < b[0]; });
        for (auto &m : meetings) {
            long st = m[0], end = m[1], id;
            while (!using_.empty() && using_.top().first <= st) {
                idle.push(using_.top().second); // 维护在 st 时刻空闲的会议室
                using_.pop();
            }
            if (idle.empty()) {
                auto[e, i] = using_.top(); // 没有可用的会议室,那么弹出一个最早结束的会议室(若有多个同时结束的,会弹出下标最小的)
                using_.pop();
                end += e - st; // 更新当前会议的结束时间
                id = i;
            } else {
                id = idle.top();
                idle.pop();
            }
            ++cnt[id];
            using_.emplace(end, id); // 使用一个会议室
        }
        int ans = 0;
        for (int i = 0; i < n; ++i) if (cnt[i] > cnt[ans]) ans = i;
        return ans;
    }
};
func mostBooked(n int, meetings [][]int) (ans int) {
cnt := make([]int, n)
idle := hp{make([]int, n)}
for i := 0; i < n; i++ {
idle.IntSlice[i] = i
}
using := hp2{}
sort.Slice(meetings, func(i, j int) bool { return meetings[i][0] < meetings[j][0] })
for _, m := range meetings {
st, end := m[0], m[1]
for len(using) > 0 && using[0].end <= st {
heap.Push(&idle, heap.Pop(&using).(pair).i) // 维护在 st 时刻空闲的会议室
}
var i int
if idle.Len() == 0 {
p := heap.Pop(&using).(pair) // 没有可用的会议室,那么弹出一个最早结束的会议室(若有多个同时结束的,会弹出下标最小的)
end += p.end - st // 更新当前会议的结束时间
i = p.i
} else {
i = heap.Pop(&idle).(int)
}
cnt[i]++
heap.Push(&using, pair{end, i}) // 使用一个会议室
}
for i, c := range cnt {
if c > cnt[ans] {
ans = i
}
}
return
}

type hp struct{ sort.IntSlice }
func (h *hp) Push(v interface{}) { h.IntSlice = append(h.IntSlice, v.(int)) }
func (h *hp) Pop() interface{}   { a := h.IntSlice; v := a[len(a)-1]; h.IntSlice = a[:len(a)-1]; return v }

type pair struct{ end, i int }
type hp2 []pair
func (h hp2) Len() int            { return len(h) }
func (h hp2) Less(i, j int) bool  { a, b := h[i], h[j]; return a.end < b.end || a.end == b.end && a.i < b.i }
func (h hp2) Swap(i, j int)       { h[i], h[j] = h[j], h[i] }
func (h *hp2) Push(v interface{}) { *h = append(*h, v.(pair)) }
func (h *hp2) Pop() interface{}   { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }

模拟

作者 tsreaper
2022年9月4日 12:07

解法:模拟

因为“当会议室处于未占用状态时,将会优先提供给原开始时间更早的会议”,因此有重要性质:会议开始的相对顺序不会改变。我们只需要按顺序模拟每个会议分配给哪个会议室即可。

复杂度 $\mathcal{O}(m\log m + nm)$,其中 $m$ 是会议的数量。具体实现见参考代码,注意会议的结束时间可能超出 int 的范围。

参考代码(c++)

###c++

class Solution {
public:
    int mostBooked(int n, vector<vector<int>>& meetings) {
        int m = meetings.size();
        // 将会议按开始时间排序
        sort(meetings.begin(), meetings.end());
        // 每个会议室被分配了多少会议
        vector<int> cnt(n);
        // 每个会议室的最早可用时间
        vector<long long> tim(n);
        // 按顺序处理会议
        for (auto &meet : meetings) {
            // best 表示当前不可用,但可用时间最早的会议室编号
            int best = 0;
            for (int i = 0; i < n; i++) {
                if (tim[i] <= meet[0]) {
                    // 当前会议室可用,直接分配
                    cnt[i]++;
                    tim[i] = meet[1];
                    goto OK;
                } else if (tim[i] < tim[best]) best = i; // 当前会议室不可用,更新 best
            }
            // 当前没有会议室可用,等待会议室 best 可用再分配
            cnt[best]++;
            tim[best] += meet[1] - meet[0];
            OK: continue;
        }
        // 统计答案
        int ans = 0;
        for (int i = 0; i < n; i++) if (cnt[i] > cnt[ans]) ans = i;
        return ans;
    }
};

也可以使用线段树将复杂度降至 $\mathcal{O}(m\log m + m\log n)$。

参考代码(c++)

###c++

class Solution {
    vector<long long> mino;

    // 修改 pos 位置的值为 val
    void modify(int id, int l, int r, int pos, long long val) {
        if (l == r) mino[id] = val;
        else {
            int nxt = id << 1, mid = (l + r) >> 1;
            if (pos <= mid) modify(nxt, l, mid, pos, val);
            else modify(nxt | 1, mid + 1, r, pos, val);
            mino[id] = min(mino[nxt], mino[nxt | 1]);
        }
    }

    // 求编号最小的,且值小等于 val 的位置
    int query1(int id, int l, int r, int val) {
        if (l == r) return l;
        else {
            int nxt = id << 1, mid = (l + r) >> 1;
            if (mino[id] > val) return -1;
            else if (mino[nxt] <= val) return query1(nxt, l, mid, val);
            else return query1(nxt | 1, mid + 1, r, val);
        }
    }

    // 求值最小的位置在哪里
    int query2(int id, int l, int r) {
        if (l == r) return l;
        else {
            int nxt = id << 1, mid = (l + r) >> 1;
            return mino[nxt] <= mino[nxt | 1] ? query2(nxt, l, mid) : query2(nxt | 1, mid + 1, r);
        }
    }

public:
    int mostBooked(int n, vector<vector<int>>& meetings) {
        mino.resize(n * 4 + 10);
        int m = meetings.size();
        sort(meetings.begin(), meetings.end());

        vector<int> cnt(n);
        for (auto &meet : meetings) {
            // 是否直接有会议室可用
            int x = query1(1, 0, n - 1, meet[0]);
            if (x >= 0) cnt[x]++, modify(1, 0, n - 1, x, meet[1]); // 直接有会议室可用,更新该会议室的可用时间
            else {
                // 没有会议室直接可用,找接下来最早可用的会议室
                x = query2(1, 0, n - 1);
                // 更新该会议室的可用时间
                cnt[x]++;
                modify(1, 0, n - 1, x, mino[1] + meet[1] - meet[0]);
            }
        }

        // 统计答案
        int ans = 0;
        for (int i = 0; i < n; i++) if (cnt[i] > cnt[ans]) ans = i;
        return ans;
    }
};
昨天 — 2025年12月26日LeetCode 每日一题题解

每日一题-商店的最少代价🟡

2025年12月26日 00:00

给你一个顾客访问商店的日志,用一个下标从 0 开始且只包含字符 'N' 和 'Y' 的字符串 customers 表示:

  • 如果第 i 个字符是 'Y' ,它表示第 i 小时有顾客到达。
  • 如果第 i 个字符是 'N' ,它表示第 i 小时没有顾客到达。

如果商店在第 j 小时关门(0 <= j <= n),代价按如下方式计算:

  • 在开门期间,如果某一个小时没有顾客到达,代价增加 1 。
  • 在关门期间,如果某一个小时有顾客到达,代价增加 1 。

请你返回在确保代价 最小 的前提下,商店的 最早 关门时间。

注意,商店在第 j 小时关门表示在第 j 小时以及之后商店处于关门状态。

 

示例 1:

输入:customers = "YYNY"
输出:2
解释:
- 第 0 小时关门,总共 1+1+0+1 = 3 代价。
- 第 1 小时关门,总共 0+1+0+1 = 2 代价。
- 第 2 小时关门,总共 0+0+0+1 = 1 代价。
- 第 3 小时关门,总共 0+0+1+1 = 2 代价。
- 第 4 小时关门,总共 0+0+1+0 = 1 代价。
在第 2 或第 4 小时关门代价都最小。由于第 2 小时更早,所以最优关门时间是 2 。

示例 2:

输入:customers = "NNNNN"
输出:0
解释:最优关门时间是 0 ,因为自始至终没有顾客到达。

示例 3:

输入:customers = "YYYY"
输出:4
解释:最优关门时间是 4 ,因为每一小时均有顾客到达。

 

提示:

  • 1 <= customers.length <= 105
  • customers 只包含字符 'Y' 和 'N' 。

[Java] 前缀和 & 空间优化

作者 Tizzi
2022年11月29日 11:18

解法一:前缀和

利用前缀和,后缀和统计N,Y的数字即可,最后计算出最小的答案。

  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(n)$
class Solution {
    public int bestClosingTime(String cus) {
        int n = cus.length(), maxv = 100005, ans = 0;
        char[] arr = cus.toCharArray();
        int[] left = new int[n + 5], right = new int[n + 5];
        for (int i = 1; i <= n; i++) left[i] += left[i - 1] + (arr[i - 1] == 'N' ? 1 : 0);
        for (int i = n; i >= 1; i--) right[i] += right[i + 1] + (arr[i - 1] == 'Y' ? 1 : 0);
        for (int i = 1; i <= n + 1; i++) {
            if (left[i - 1] + right[i] < maxv) {
                maxv = left[i - 1] + right[i];
                ans = i - 1;
            }
        }
        return ans;
    }
}

解法二:空间优化

先统计所有Y的个数,然后对于当前字符计算代价即可,最后更新总代价。

class Solution {
    public int bestClosingTime(String cus) {
        int n = cus.length(), maxv = 100005, ans = 0;
        char[] arr = cus.toCharArray();
        int left_right = 0; 
        for (int i = n; i >= 1; i--) left_right += (arr[i - 1] == 'Y' ? 1 : 0);
        for (int i = 1; i <= n + 1; i++) {
            if (left_right < maxv) {
                maxv = left_right;
                ans = i - 1;
            }
            if (i <= n && arr[i - 1] == 'Y') left_right--;
            else left_right++;
        }
        return ans;
    }
}
  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

如果有问题,欢迎评论区交流, 如果有帮助到你,请给题解点个赞和收藏哈~~~

前后缀分解,一次遍历优化(Python/Java/C++/Go)

作者 endlesscheng
2022年11月27日 09:02

题意

把 $\textit{customers}$ 分成两段,第一段计算 $\texttt{N}$ 的个数 $\textit{preN}$,第二段计算 $\texttt{Y}$ 的个数 $\textit{sufY}$。

计算 $\textit{preN}+\textit{sufY}$ 的最小值对应的最小分割位置 $i$,其中 $[0,i-1]$ 是第一段,$[i,n-1]$ 是第二段。

注:也可以不分割,相当于其中一段是空的。

思路

先假设所有字母都在第二段,统计 $\textit{customers}$ 中 $\texttt{Y}$ 的个数 $\textit{sufY}$。

想象一根分割线在从左到右移动,$\textit{customers}[i]$ 原来在第二段,现在在第一段。如果 $\textit{customers}[i] = \texttt{N}$,那么把 $\textit{preN}$ 加一($\textit{preN}$ 初始值为 $0$),否则把 $\textit{sufY}$ 减一。

答案为 $\textit{preN}+\textit{sufY}$ 最小时对应的分割位置。

代码实现时,$\textit{preN}+\textit{sufY}$ 可以合并为一个变量 $\textit{penalty}$。

优化前

class Solution:
    def bestClosingTime(self, customers: str) -> int:
        min_penalty = penalty = customers.count('Y')
        ans = 0  # [0,n-1] 是第二段
        for i, c in enumerate(customers):
            penalty += 1 if c == 'N' else -1
            if penalty < min_penalty:
                min_penalty = penalty
                ans = i + 1  # [0,i] 是第一段,[i+1,n-1] 是第二段
        return ans
class Solution {
    public int bestClosingTime(String customers) {
        char[] s = customers.toCharArray();
        int penalty = 0;
        for (char c : s) {
            if (c == 'Y') {
                penalty++;
            }
        }

        int minPenalty = penalty;
        int ans = 0; // [0,n-1] 是第二段
        for (int i = 0; i < s.length; i++) {
            penalty += s[i] == 'N' ? 1 : -1;
            if (penalty < minPenalty) {
                minPenalty = penalty;
                ans = i + 1; // [0,i] 是第一段,[i+1,n-1] 是第二段
            }
        }
        return ans;
    }
}
class Solution {
public:
    int bestClosingTime(string customers) {
        int penalty = ranges::count(customers, 'Y');
        int min_penalty = penalty;
        int ans = 0; // [0,n-1] 是第二段
        for (int i = 0; i < customers.size(); i++) {
            penalty += customers[i] == 'N' ? 1 : -1;
            if (penalty < min_penalty) {
                min_penalty = penalty;
                ans = i + 1; // [0,i] 是第一段,[i+1,n-1] 是第二段
            }
        }
        return ans;
    }
};
func bestClosingTime(customers string) int {
penalty := strings.Count(customers, "Y")
minPenalty := penalty
ans := 0 // [0,n-1] 是第二段
for i, c := range customers {
if c == 'N' {
penalty++
} else {
penalty--
}
if penalty < minPenalty {
minPenalty = penalty
ans = i + 1 // [0,i] 是第一段,[i+1,n-1] 是第二段
}
}
return ans
}

优化:一次遍历

第一次遍历(统计 $\texttt{Y}$ 的个数)是多余的。

打个比方,绝对温度下冰点为 $273,\mathrm{K}$,摄氏温度下冰点为 $0~^\circ\mathrm{C}$。如果只关心哪一天最冷,用哪个单位计算都可以。

或者说,把折线图往下平移(第一个点移到坐标零点),最低点的横坐标仍然不变。

class Solution:
    def bestClosingTime(self, customers: str) -> int:
        min_penalty = penalty = ans = 0
        for i, c in enumerate(customers):
            penalty += 1 if c == 'N' else -1
            if penalty < min_penalty:
                min_penalty = penalty
                ans = i + 1
        return ans
class Solution {
    public int bestClosingTime(String customers) {
        int penalty = 0;
        int minPenalty = 0;
        int ans = 0;
        for (int i = 0; i < customers.length(); i++) {
            penalty += customers.charAt(i) == 'N' ? 1 : -1;
            if (penalty < minPenalty) {
                minPenalty = penalty;
                ans = i + 1;
            }
        }
        return ans;
    }
}
class Solution {
public:
    int bestClosingTime(string customers) {
        int penalty = 0, min_penalty = 0, ans = 0;
        for (int i = 0; i < customers.size(); i++) {
            penalty += customers[i] == 'N' ? 1 : -1;
            if (penalty < min_penalty) {
                min_penalty = penalty;
                ans = i + 1;
            }
        }
        return ans;
    }
};
func bestClosingTime(customers string) (ans int) {
penalty, minPenalty := 0, 0
for i, c := range customers {
if c == 'N' {
penalty++
} else {
penalty--
}
if penalty < minPenalty {
minPenalty = penalty
ans = i + 1
}
}
return
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{customers}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

相似题目

3694. 删除子字符串后不同的终点

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

枚举 & 前缀和

作者 tsreaper
2022年11月27日 00:09

解法:枚举 & 前缀和

为了方便计算前(后)缀和,我们认为 customers 的下标是从 $1$ 开始的,最后答案减 $1$ 即可。

维护 $f(i)$ 表示第 $1$ 到第 $i$ 个字符有几个 N(初值 $f(0) = 0$),$g(i)$ 表示第 $i$ 到第 $n$ 个字符有几个 Y(初值 $g(n + 1) = 0$),那么第 $h$ 小时关店的代价即为 $f(h - 1) + g(h)$。

从 $1$ 到 $(n + 1)$ 枚举所有 $h$ 并选出代价最小的即可。复杂度 $\mathcal{O}(n)$。

参考代码(c++)

###c++

class Solution {
public:
    int bestClosingTime(string customers) {
        int n = customers.size();

        int f[n + 2], g[n + 2];
        // 计算前缀有几个 N
        f[0] = 0;
        for (int i = 1; i <= n; i++) f[i] = f[i - 1] + (customers[i - 1] == 'N' ? 1 : 0);
        // 计算后缀有几个 Y
        g[n + 1] = 0;
        for (int i = n; i > 0; i--) g[i] = g[i + 1] + (customers[i - 1] == 'Y' ? 1 : 0);

        // 枚举答案
        int ans = 0, best = 1e9;
        for (int i = 1; i <= n + 1; i++) {
            int tmp = f[i - 1] + g[i];
            if (tmp < best) ans = i, best = tmp;
        }
        return ans - 1;
    }
};
昨天以前LeetCode 每日一题题解

[Python3/Java/C++/Go/TypeScript] 一题一解:贪心 + 排序(清晰题解)

作者 lcbin
2025年12月25日 07:33

方法一:贪心 + 排序

为了使得幸福值之和尽可能大,我们应该优先选择幸福值大的孩子。因此,我们可以对孩子按照幸福值从大到小排序,然后依次选择 $k$ 个孩子。对于当前第 $i$ 个孩子,能够得到的幸福值为 $\max(\textit{happiness}[i] - i, 0)$,最后返回这 $k$ 个孩子的幸福值之和。

###python

class Solution:
    def maximumHappinessSum(self, happiness: List[int], k: int) -> int:
        happiness.sort(reverse=True)
        ans = 0
        for i, x in enumerate(happiness[:k]):
            x -= i
            ans += max(x, 0)
        return ans

###java

class Solution {
    public long maximumHappinessSum(int[] happiness, int k) {
        Arrays.sort(happiness);
        long ans = 0;
        for (int i = 0, n = happiness.length; i < k; ++i) {
            int x = happiness[n - i - 1] - i;
            ans += Math.max(x, 0);
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    long long maximumHappinessSum(vector<int>& happiness, int k) {
        sort(happiness.rbegin(), happiness.rend());
        long long ans = 0;
        for (int i = 0, n = happiness.size(); i < k; ++i) {
            int x = happiness[i] - i;
            ans += max(x, 0);
        }
        return ans;
    }
};

###go

func maximumHappinessSum(happiness []int, k int) (ans int64) {
sort.Ints(happiness)
for i := 0; i < k; i++ {
x := happiness[len(happiness)-i-1] - i
ans += int64(max(x, 0))
}
return
}

###ts

function maximumHappinessSum(happiness: number[], k: number): number {
    happiness.sort((a, b) => b - a);
    let ans = 0;
    for (let i = 0; i < k; ++i) {
        const x = happiness[i] - i;
        ans += Math.max(x, 0);
    }
    return ans;
}

###rust

impl Solution {
    pub fn maximum_happiness_sum(mut happiness: Vec<i32>, k: i32) -> i64 {
        happiness.sort_unstable_by(|a, b| b.cmp(a));

        let mut ans: i64 = 0;
        for i in 0..(k as usize) {
            let x = happiness[i] as i64 - i as i64;
            if x > 0 {
                ans += x;
            }
        }
        ans
    }
}

###cs

public class Solution {
    public long MaximumHappinessSum(int[] happiness, int k) {
        Array.Sort(happiness, (a, b) => b.CompareTo(a));
        long ans = 0;
        for (int i = 0; i < k; i++) {
            int x = happiness[i] - i;
            if (x <= 0) {
                break;
            }
            ans += x;
        }
        return ans;
    }
}

时间复杂度 $O(n \times \log n + k)$,空间复杂度 $O(\log n)$。其中 $n$ 是数组 $\textit{happiness}$ 的长度。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-幸福值最大化的选择方案🟡

2025年12月25日 00:00

给你一个长度为 n 的数组 happiness ,以及一个 正整数 k

n 个孩子站成一队,其中第 i 个孩子的 幸福值 happiness[i] 。你计划组织 k 轮筛选从这 n 个孩子中选出 k 个孩子。

在每一轮选择一个孩子时,所有 尚未 被选中的孩子的 幸福值 将减少 1 。注意,幸福值 不能 变成负数,且只有在它是正数的情况下才会减少。

选择 k 个孩子,并使你选中的孩子幸福值之和最大,返回你能够得到的 最大值

 

示例 1:

输入:happiness = [1,2,3], k = 2
输出:4
解释:按以下方式选择 2 个孩子:
- 选择幸福值为 3 的孩子。剩余孩子的幸福值变为 [0,1] 。
- 选择幸福值为 1 的孩子。剩余孩子的幸福值变为 [0] 。注意幸福值不能小于 0 。
所选孩子的幸福值之和为 3 + 1 = 4 。

示例 2:

输入:happiness = [1,1,1,1], k = 2
输出:1
解释:按以下方式选择 2 个孩子:
- 选择幸福值为 1 的任意一个孩子。剩余孩子的幸福值变为 [0,0,0] 。
- 选择幸福值为 0 的孩子。剩余孩子的幸福值变为 [0,0] 。
所选孩子的幸福值之和为 1 + 0 = 1 。

示例 3:

输入:happiness = [2,3,4,5], k = 1
输出:5
解释:按以下方式选择 1 个孩子:
- 选择幸福值为 5 的孩子。剩余孩子的幸福值变为 [1,2,3] 。
所选孩子的幸福值之和为 5 。

 

提示:

  • 1 <= n == happiness.length <= 2 * 105
  • 1 <= happiness[i] <= 108
  • 1 <= k <= n

3075. 幸福值最大化的选择方案

作者 stormsunshine
2024年3月10日 14:30

解法

思路和算法

为了使选择的 $k$ 个孩子的幸福值之和最大,应遵循如下贪心策略:按照幸福值递减的顺序选择幸福值最大的 $k$ 个孩子。理由如下。

  1. 如果将幸福值最大的 $k$ 个孩子中的任意一个孩子更换成一个幸福值较小的孩子,则更换之后的孩子的幸福值一定不变或减少,不可能增加,因此幸福值之和不可能更大。

  2. 当选择幸福值最大的 $k$ 个孩子时,如果这 $k$ 个孩子的初始幸福值都不小于 $k - 1$ 则幸福值之和的总减少量等于 $\dfrac{k(k + 1)}{2}$,如果这 $k$ 个孩子中存在孩子的初始幸福值小于 $k - 1$ 则幸福值之和的总减少量小于 $\dfrac{k(k + 1)}{2}$。假设两个孩子的幸福值分别为 $x$ 和 $y$,其中 $x > y$,则先选择 $x$ 后选择 $y$ 的幸福值之和等于 $x + \max(y - 1, 0)$,先选择 $y$ 后选择 $x$ 的幸福值之和等于 $y + \max(x - 1, 0)$,由于 $x > y > 0$,因此 $\max(x - 1, 0) = x - 1$,$x + \max(y - 1, 0) \ge y + \max(x - 1, 0)$,即先选择 $x$ 后选择 $y$ 的方案更优。

根据贪心策略,计算选中的孩子的幸福值之和的最大值的方法如下。

  1. 将数组 $\textit{happiness}$ 按升序排序。

  2. 用 $n$ 表示数组 $\textit{happiness}$ 的长度,反向遍历排序后的数组 $\textit{happiness}$,对于每个 $1 \le i \le k$,需要选择幸福值为 $\textit{happiness}[n - i]$ 的孩子,该孩子被选择时的幸福值为 $\max(\textit{happiness}[n - i] - (i - 1), 0)$,计算选中的孩子的幸福值之和。

实现方面,反向遍历排序后的数组 $\textit{happiness}$ 时,当遇到 $\textit{happiness}[n - i] \le i - 1$ 时,其余孩子的幸福值一定都是 $0$,此时即可结束遍历。

遍历结束之后,即可得到选中的孩子的幸福值之和的最大值。

代码

###Java

class Solution {
    public long maximumHappinessSum(int[] happiness, int k) {
        long sum = 0;
        Arrays.sort(happiness);
        int n = happiness.length;
        for (int i = 1; i <= k && happiness[n - i] > i - 1; i++) {
            int curr = happiness[n - i] - (i - 1);
            sum += curr;
        }
        return sum;
    }
}

###C#

public class Solution {
    public long MaximumHappinessSum(int[] happiness, int k) {
        long sum = 0;
        Array.Sort(happiness);
        int n = happiness.Length;
        for (int i = 1; i <= k && happiness[n - i] > i - 1; i++) {
            int curr = happiness[n - i] - (i - 1);
            sum += curr;
        }
        return sum;
    }
}

复杂度分析

  • 时间复杂度:$O(n \log n)$,其中 $n$ 是数组 $\textit{happiness}$ 的长度。将数组 $\textit{happiness}$ 排序的时间是 $O(n \log n)$,排序后遍历数组 $\textit{happiness}$ 计算选中的孩子的幸福值之和的最大值的时间是 $O(n)$,因此时间复杂度是 $O(n \log n)$。

  • 空间复杂度:$O(\log n)$,其中 $n$ 是数组 $\textit{happiness}$ 的长度。排序的递归调用栈空间是 $O(\log n)$。

排序 + 贪心(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2024年3月10日 12:26

本质是选一些数求和,为了让和最大,我们要选 $\textit{happiness}$ 最大的 $k$ 个数。

这 $k$ 个数要按照什么顺序选呢?

由于小的数减成 $0$ 就不再减少了,优先选大的数更好。

比如 $2,1,1$,如果按照 $1,1,2$ 的顺序选,答案为 $1+0+0=1$;但按照 $2,1,1$ 的顺序选,答案为 $2+0+0=2$,更优。

###py

class Solution:
    def maximumHappinessSum(self, happiness: List[int], k: int) -> int:
        happiness.sort(reverse=True)
        ans = 0
        for i, x in enumerate(happiness[:k]):
            if x <= i:
                break
            ans += x - i
        return ans

###java

class Solution {
    public long maximumHappinessSum(int[] happiness, int k) {
        Arrays.sort(happiness);
        int n = happiness.length;
        long ans = 0;
        for (int i = n - 1; i >= n - k && happiness[i] > n - 1 - i; i--) {
            ans += happiness[i] - (n - 1 - i);
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    long long maximumHappinessSum(vector<int>& happiness, int k) {
        ranges::sort(happiness, greater());
        long long ans = 0;
        for (int i = 0; i < k && happiness[i] > i; i++) {
            ans += happiness[i] - i;
        }
        return ans;
    }
};

###c

int cmp(const void* a, const void* b) {
    return *(int*)b - *(int*)a;
}

long long maximumHappinessSum(int* happiness, int happinessSize, int k) {
    qsort(happiness, happinessSize, sizeof(int), cmp);
    long long ans = 0;
    for (int i = 0; i < k && happiness[i] > i; i++) {
        ans += happiness[i] - i;
    }
    return ans;
}

###go

func maximumHappinessSum(happiness []int, k int) (ans int64) {
slices.SortFunc(happiness, func(a, b int) int { return b - a })
for i, x := range happiness[:k] {
if x <= i {
break
}
ans += int64(x - i)
}
return
}

###js

var maximumHappinessSum = function(happiness, k) {
    happiness.sort((a, b) => b - a);
    let ans = 0;
    for (let i = 0; i < k && happiness[i] > i; i++) {
        ans += happiness[i] - i;
    }
    return ans;
};

###rust

impl Solution {
    pub fn maximum_happiness_sum(mut happiness: Vec<i32>, k: i32) -> i64 {
        happiness.sort_unstable_by_key(|x| -x);
        let mut ans = 0;
        for (i, &x) in happiness[..k as usize].iter().enumerate() {
            if x <= i as i32 {
                break;
            }
            ans += (x - i as i32) as i64;
        }
        ans
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n\log n)$,其中 $n$ 是 $\textit{happiness}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。忽略排序的栈开销。Python 的切片可以用枚举代替。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

贪心 & 排序

作者 tsreaper
2024年3月10日 12:08

解法:贪心 & 排序

每次应该选择当前幸福值最高的孩子。由于每个孩子在没被选中时,幸福值都会下降相同的量,所以幸福值的相对大小关系不会改变。

因此将孩子按幸福值从大到小排序,依次选择孩子即可。复杂度 $\mathcal{O}(n\log n)$。

参考代码(c++)

###c++

class Solution {
public:
    long long maximumHappinessSum(vector<int>& happiness, int K) {
        int n = happiness.size();
        // 将孩子按幸福值排序
        sort(happiness.begin(), happiness.end());
        long long ans = 0;
        // 按幸福值从大到小选择孩子
        for (int i = n - 1; i >= n - K; i--) ans += max(0, happiness[i] - (n - 1 - i));
        return ans;
    }
};

[Python3/Java/C++/Go/TypeScript] 一题一解:贪心+排序(清晰题解)

作者 lcbin
2025年12月24日 07:20

方法一:贪心 + 排序

为了使得需要的箱子数量尽可能少,我们应该优先使用容量大的箱子。因此,我们可以对箱子按照容量从大到小排序,然后依次使用箱子,直到所有的苹果都被分装完,返回此时使用的箱子数量。

###python

class Solution:
    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
        capacity.sort(reverse=True)
        s = sum(apple)
        for i, c in enumerate(capacity, 1):
            s -= c
            if s <= 0:
                return i

###java

class Solution {
    public int minimumBoxes(int[] apple, int[] capacity) {
        Arrays.sort(capacity);
        int s = 0;
        for (int x : apple) {
            s += x;
        }
        for (int i = 1, n = capacity.length;; ++i) {
            s -= capacity[n - i];
            if (s <= 0) {
                return i;
            }
        }
    }
}

###cpp

class Solution {
public:
    int minimumBoxes(vector<int>& apple, vector<int>& capacity) {
        sort(capacity.rbegin(), capacity.rend());
        int s = accumulate(apple.begin(), apple.end(), 0);
        for (int i = 1;; ++i) {
            s -= capacity[i - 1];
            if (s <= 0) {
                return i;
            }
        }
    }
};

###go

func minimumBoxes(apple []int, capacity []int) int {
sort.Ints(capacity)
s := 0
for _, x := range apple {
s += x
}
for i := 1; ; i++ {
s -= capacity[len(capacity)-i]
if s <= 0 {
return i
}
}
}

###ts

function minimumBoxes(apple: number[], capacity: number[]): number {
    capacity.sort((a, b) => b - a);
    let s = apple.reduce((acc, cur) => acc + cur, 0);
    for (let i = 1; ; ++i) {
        s -= capacity[i - 1];
        if (s <= 0) {
            return i;
        }
    }
}

###rust

impl Solution {
    pub fn minimum_boxes(apple: Vec<i32>, mut capacity: Vec<i32>) -> i32 {
        capacity.sort();

        let mut s: i32 = apple.iter().sum();

        let n = capacity.len();
        let mut i = 1;
        loop {
            s -= capacity[n - i];
            if s <= 0 {
                return i as i32;
            }
            i += 1;
        }
    }
}

时间复杂度 $O(m \times \log m + n)$,空间复杂度 $O(\log m)$。其中 $m$ 和 $n$ 分别是数组 $\textit{capacity}$ 和 $\textit{apple}$ 的长度。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-重新分装苹果🟢

2025年12月24日 00:00

给你一个长度为 n 的数组 apple 和另一个长度为 m 的数组 capacity

一共有 n 个包裹,其中第 i 个包裹中装着 apple[i] 个苹果。同时,还有 m 个箱子,第 i 个箱子的容量为 capacity[i] 个苹果。

请你选择一些箱子来将这 n 个包裹中的苹果重新分装到箱子中,返回你需要选择的箱子的 最小 数量。

注意,同一个包裹中的苹果可以分装到不同的箱子中。

 

示例 1:

输入:apple = [1,3,2], capacity = [4,3,1,5,2]
输出:2
解释:使用容量为 4 和 5 的箱子。
总容量大于或等于苹果的总数,所以可以完成重新分装。

示例 2:

输入:apple = [5,5,5], capacity = [2,4,2,7]
输出:4
解释:需要使用所有箱子。

 

提示:

  • 1 <= n == apple.length <= 50
  • 1 <= m == capacity.length <= 50
  • 1 <= apple[i], capacity[i] <= 50
  • 输入数据保证可以将包裹中的苹果重新分装到箱子中。

排序贪心(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2024年3月10日 16:46

既然同一个包裹中的苹果可以分装到不同的箱子中,那就先把所有苹果堆在一起,然后一个个地装箱。

为了少用箱子,要先装大箱子,再装小箱子。

注:题目保证可以将所有苹果重新分装到箱子中。

###py

class Solution:
    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
        s = sum(apple)  # 把所有苹果堆在一起
        capacity.sort(reverse=True)  # 先装大箱子,再装小箱子
        for i, x in enumerate(capacity):
            s -= x
            if s <= 0:  # 所有苹果都装入了箱子
                return i + 1  # 从 0 到 i 有 i+1 个箱子

###java

class Solution {
    public int minimumBoxes(int[] apple, int[] capacity) {
        int s = 0;
        for (int x : apple) {
            s += x; // 把所有苹果堆在一起
        }

        Arrays.sort(capacity);

        int m = capacity.length;
        int i = m - 1; // 先装大箱子,再装小箱子
        while (s > 0) { // 还有剩余苹果
            s -= capacity[i--]; // 使用一个箱子
        }
        return m - 1 - i;
    }
}

###cpp

class Solution {
public:
    int minimumBoxes(vector<int>& apple, vector<int>& capacity) {
        int s = reduce(apple.begin(), apple.end()); // 把所有苹果堆在一起
        ranges::sort(capacity, greater()); // 先装大箱子,再装小箱子
        int i = 0;
        while (s > 0) { // 还有剩余苹果
            s -= capacity[i++]; // 使用一个箱子
        }
        return i;
    }
};

###c

int cmp(const void* a, const void* b) {
    return *(int*)b - *(int*)a;
}

int minimumBoxes(int* apple, int appleSize, int* capacity, int capacitySize) {
    int s = 0;
    for (int i = 0; i < appleSize; i++) {
        s += apple[i]; // 把所有苹果堆在一起
    }

    qsort(capacity, capacitySize, sizeof(int), cmp); // 先装大箱子,再装小箱子

    int i = 0;
    while (s > 0) { // 还有剩余苹果
        s -= capacity[i++]; // 使用一个箱子
    }
    return i;
}

###go

func minimumBoxes(apple, capacity []int) int {
s := 0
for _, x := range apple {
s += x // 把所有苹果堆在一起
}
slices.SortFunc(capacity, func(a, b int) int { return b - a }) // 先装大箱子,再装小箱子
for i, c := range capacity {
s -= c
if s <= 0 { // 所有苹果都装入了箱子
return i + 1 // 从 0 到 i 有 i+1 个箱子
}
}
return -1
}

###js

var minimumBoxes = function(apple, capacity) {
    let s = _.sum(apple); // 把所有苹果堆在一起
    capacity.sort((a, b) => b - a); // 先装大箱子,再装小箱子
    let i = 0;
    while (s > 0) { // 还有剩余苹果
        s -= capacity[i++]; // 使用一个箱子
    }
    return i;
};

###rust

impl Solution {
    pub fn minimum_boxes(apple: Vec<i32>, mut capacity: Vec<i32>) -> i32 {
        let mut s = apple.iter().sum::<i32>(); // 把所有苹果堆在一起
        capacity.sort_unstable_by_key(|a| -a); // 先装大箱子,再装小箱子
        for (i, x) in capacity.iter().enumerate() {
            s -= x;
            if s <= 0 { // 所有苹果都装入了箱子
                return (i + 1) as _; // 从 0 到 i 有 i+1 个箱子
            }
        }
        unreachable!()
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n+m\log m)$,其中 $n$ 为 $\textit{apple}$ 的长度,$m$ 为 $\textit{capacity}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。忽略排序的栈开销。

专题训练

见贪心题单的「§1.1 从最小/最大开始贪心」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

贪心+优先队列

作者 ylrk
2024年3月10日 13:25

Problem: 100233. 重新分装苹果

[TOC]

思路

这个问题的核心思路是贪心算法。我们需要找到最少需要的箱子数量,因此我们应该优先使用容量最大的箱子,因为这样可以装更多的苹果,从而减少需要的箱子数量。为了快速找到最大的箱子,我们可以使用一个优先队列来存储所有的箱子,优先队列的顶部始终是最大的元素。

解题方法

首先,我们计算出所有苹果的总数。然后,我们将所有的箱子的容量放入一个优先队列中。然后我们进入一个循环,只要还有苹果没有装箱,我们就从优先队列中取出最大的箱子,将这个箱子的容量从苹果的总数中减去,并将箱子的数量加1。当所有的苹果都装箱,或者没有更多的箱子时,我们停止循环,返回箱子的数量。

Code

###C++

class Solution {
public:
    int minimumBoxes(vector<int>& apple, vector<int>& capacity) {
        int total_apples = std::accumulate(apple.begin(), apple.end(), 0);
        std::priority_queue<int> pq(capacity.begin(), capacity.end());
        int count = 0;
        while(total_apples > 0 && !pq.empty()) {
            total_apples -= pq.top();
            pq.pop();
            ++count;
        }
        return count;
    }
};

每日一题-两个最好的不重叠活动🟡

2025年12月23日 00:00

给你一个下标从 0 开始的二维整数数组 events ,其中 events[i] = [startTimei, endTimei, valuei] 。第 i 个活动开始于 startTimei ,结束于 endTimei ,如果你参加这个活动,那么你可以得到价值 valuei 。你 最多 可以参加 两个时间不重叠 活动,使得它们的价值之和 最大 。

请你返回价值之和的 最大值 。

注意,活动的开始时间和结束时间是 包括 在活动时间内的,也就是说,你不能参加两个活动且它们之一的开始时间等于另一个活动的结束时间。更具体的,如果你参加一个活动,且结束时间为 t ,那么下一个活动必须在 t + 1 或之后的时间开始。

 

示例 1:

输入:events = [[1,3,2],[4,5,2],[2,4,3]]
输出:4
解释:选择绿色的活动 0 和 1 ,价值之和为 2 + 2 = 4 。

示例 2:

Example 1 Diagram

输入:events = [[1,3,2],[4,5,2],[1,5,5]]
输出:5
解释:选择活动 2 ,价值和为 5 。

示例 3:

输入:events = [[1,5,3],[1,5,1],[6,6,5]]
输出:8
解释:选择活动 0 和 2 ,价值之和为 3 + 5 = 8 。

 

提示:

  • 2 <= events.length <= 105
  • events[i].length == 3
  • 1 <= startTimei <= endTimei <= 109
  • 1 <= valuei <= 106
❌
❌