{ }codelper
贪心自定义排序P0090

一间会议室安排最多会议

有一间会议室,以及想要召开的 n 个会议。
第 i 个会议在时刻 sᵢ 开始、在 eᵢ 结束(sᵢ < eᵢ)。
会议室一次只能开一个会议,会议开始后不能中途暂停。
一个会议结束的那一刻就可以开始下一个会议(例如 1~3 和 3~5 都能开)。
输出能互不重叠地召开的会议的最大个数。
第一行给出 n,接下来 n 行给出用空格隔开的 sᵢ 和 eᵢ。
完全相同的会议可能出现多次。

输入
1 ≤ n ≤ 5,000,0 ≤ sᵢ < eᵢ ≤ 10⁹
输出
能召开的会议的最大个数(一个整数)
样例 1 · stdin
5
1 4
3 5
0 6
5 7
8 9
stdout
3
样例 2 · stdin
3
1 3
3 5
2 4
stdout
2
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

按结束时刻从早到晚查看会议,凡是在会议室空出来之后开始的都选上,就能最多(结束得越早,之后剩下的时间越多,不会吃亏)。
四个陷阱:按开始最早来选,一个长会议会挡住好几个短会议。
按最短来选也不行:跨在两个会议之间的短会议会挡住两个。
丢掉相接会议的 start > freeAt 是错的,start >= freeAt 才对。
另外各语言给出排序依据的方式不同:Python sort(key=lambda m: m[1]),Java Arrays.sort(a, Comparator.comparingInt(m -> m[1])),C# LINQ OrderBy(m => m.End)(元组可以给字段命名),C++ std::sort 配一个返回 a.second < b.second 的 lambda,C qsort 配一个返回负数/0/正数的比较函数(范围大时 a - b 可能溢出,(a > b) - (a < b) 更安全)。
JavaScript/TypeScript 一定要传比较函数 (a, b) => a[1] - b[1]——不传直接 sort() 会按字符串比较,"100,5" 会排在 "20,1" 前面。
Craykon 定义 结束优先(前, 后) { 返回 前[1] - 后[1]; },再以 meetings.排序(结束优先) 传入。
复杂度 n log n,n = 5,000 瞬间完成。

Pythonmain.py · 15 行
import sys data = sys.stdin.read().split()n = int(data[0])meetings = [(int(data[1 + 2 * i]), int(data[2 + 2 * i])) for i in range(n)] # 일찍 끝나는 회의부터 본다: 일찍 끝날수록 뒤에 남는 시간이 많다meetings.sort(key=lambda m: m[1])count = 0free_at = 0  # 회의실이 비는 시각for start, end in meetings:    if start >= free_at:  # 앞 회의가 끝나는 바로 그 시각에 시작해도 된다        count += 1        free_at = endprint(count)
JavaScriptmain.js · 16 行
const t = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = t[0];const meetings = [];for (let i = 0; i < n; i++) meetings.push([t[1 + 2 * i], t[2 + 2 * i]]); // 일찍 끝나는 회의부터 본다. 비교 함수가 없으면 sort()는 글자로 비교한다meetings.sort((a, b) => a[1] - b[1]);let count = 0;let freeAt = 0; // 회의실이 비는 시각for (const [start, end] of meetings) {  if (start >= freeAt) { // 앞 회의가 끝나는 바로 그 시각에 시작해도 된다    count++;    freeAt = end;  }}console.log(count);
TypeScriptmain.ts · 17 行
import { readFileSync } from "node:fs";const t: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = t[0];const meetings: [number, number][] = [];for (let i = 0; i < n; i++) meetings.push([t[1 + 2 * i], t[2 + 2 * i]]); // 일찍 끝나는 회의부터 본다. 비교 함수가 없으면 sort()는 글자로 비교한다meetings.sort((a, b) => a[1] - b[1]);let count = 0;let freeAt = 0; // 회의실이 비는 시각for (const [start, end] of meetings) {  if (start >= freeAt) { // 앞 회의가 끝나는 바로 그 시각에 시작해도 된다    count++;    freeAt = end;  }}console.log(count);
JavaMain.java · 31 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.Comparator;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        int[][] meetings = new int[n][2];        for (int i = 0; i < n; i++) {            StringTokenizer st = new StringTokenizer(br.readLine());            meetings[i][0] = Integer.parseInt(st.nextToken());            meetings[i][1] = Integer.parseInt(st.nextToken());        }         // 일찍 끝나는 회의부터 본다: 일찍 끝날수록 뒤에 남는 시간이 많다        Arrays.sort(meetings, Comparator.comparingInt(m -> m[1]));        int count = 0;        int freeAt = 0; // 회의실이 비는 시각        for (int[] m : meetings) {            if (m[0] >= freeAt) { // 앞 회의가 끝나는 바로 그 시각에 시작해도 된다                count++;                freeAt = m[1];            }        }        System.out.println(count);    }}
C#Main.cs · 27 行
using System;using System.Linq; class Program{    static void Main()    {        string[] t = Console.In.ReadToEnd().Split((char[])null, StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(t[0]);        var meetings = Enumerable.Range(0, n)            .Select(i => (Start: int.Parse(t[1 + 2 * i]), End: int.Parse(t[2 + 2 * i])))            .OrderBy(m => m.End) // 일찍 끝나는 회의부터 본다            .ToList();         int count = 0;        int freeAt = 0; // 회의실이 비는 시각        foreach (var m in meetings)        {            if (m.Start >= freeAt) // 앞 회의가 끝나는 바로 그 시각에 시작해도 된다            {                count++;                freeAt = m.End;            }        }        Console.WriteLine(count);    }}
C++main.cpp · 25 行
#include <algorithm>#include <iostream>#include <utility>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n;    std::cin >> n;    std::vector<std::pair<int, int>> meetings(n); // (시작, 끝)    for (auto& [start, end] : meetings) std::cin >> start >> end;     // 일찍 끝나는 회의부터 본다: 일찍 끝날수록 뒤에 남는 시간이 많다    std::sort(meetings.begin(), meetings.end(), [](const auto& a, const auto& b) { return a.second < b.second; });    int count = 0;    int freeAt = 0; // 회의실이 비는 시각    for (const auto& [start, end] : meetings) {        if (start >= freeAt) { // 앞 회의가 끝나는 바로 그 시각에 시작해도 된다            count++;            freeAt = end;        }    }    std::cout << count << '\n';}
Cmain.c · 33 行
#include <stdio.h>#include <stdlib.h> #define MAXN 5000 typedef struct {    int start, end;} Meeting; /* 끝나는 시각이 이른 것이 앞. 빼기(a - b) 대신 비교해서 넘침 걱정을 없앤다 */static int by_end(const void *x, const void *y) {    const Meeting *a = x, *b = y;    return (a->end > b->end) - (a->end < b->end);} int main(void) {    static Meeting meetings[MAXN];    int n;    scanf("%d", &n);    for (int i = 0; i < n; i++) scanf("%d %d", &meetings[i].start, &meetings[i].end);     qsort(meetings, n, sizeof(Meeting), by_end);    int count = 0;    int free_at = 0; /* 회의실이 비는 시각 */    for (int i = 0; i < n; i++) {        if (meetings[i].start >= free_at) { /* 끝나는 바로 그 시각에 시작해도 된다 */            count++;            free_at = meetings[i].end;        }    }    printf("%d\n", count);    return 0;}
Craykonmain.cray · 26 行
// 끝나는 시각이 이른 것이 앞에 오게 하는 비교 함수끝먼저(앞, 뒤){    返回 앞[1] - 뒤[1];} n = 控制台.读行().转数字();meetings = [];循环 (i = 0; i < n; i++){    meetings.添加(控制台.读数字()); // [시작, 끝]} // 일찍 끝나는 회의부터 본다: 일찍 끝날수록 뒤에 남는 시간이 많다meetings.排序(끝먼저);count = 0;freeAt = 0; // 회의실이 비는 시각遍历 (m 在 meetings){    如果 (m[0] >= freeAt)    {        count += 1;        freeAt = m[1];    }}控制台.输出(count);