728x90
728x90
시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
2 초 128 MB 156222 49243 34665 29.706%

문제

한 개의 회의실이 있는데 이를 사용하고자 하는 N개의 회의에 대하여 회의실 사용표를 만들려고 한다. 각 회의 I에 대해 시작시간과 끝나는 시간이 주어져 있고, 각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 회의의 최대 개수를 찾아보자. 단, 회의는 한번 시작하면 중간에 중단될 수 없으며 한 회의가 끝나는 것과 동시에 다음 회의가 시작될 수 있다. 회의의 시작시간과 끝나는 시간이 같을 수도 있다. 이 경우에는 시작하자마자 끝나는 것으로 생각하면 된다.

 

입력

첫째 줄에 회의의 수 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N+1 줄까지 각 회의의 정보가 주어지는데 이것은 공백을 사이에 두고 회의의 시작시간과 끝나는 시간이 주어진다. 시작 시간과 끝나는 시간은 $2^{31}-1$보다 작거나 같은 자연수 또는 0이다.

 

출력

첫째 줄에 최대 사용할 수 있는 회의의 최대 개수를 출력한다.

 

예제 입력 1

11
1 4
3 5
0 6
5 7
3 8
5 9
6 10
8 11
8 12
2 13
12 14

 

예제 출력 1

4

 

힌트

(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

 

문제 출처

https://www.acmicpc.net/problem/1931

 

1931번: 회의실 배정

(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

www.acmicpc.net

 


 

문제 해결 방법

  • 실버I의 난이도이지만 정답률이 29%대인 문제이다.
  • 시간 초과 문제 때문에 정답률이 낮아진 것으로 추정된다.
  • 그리디 알고리즘을 이용하여 문제를 해결할 수 있었으며, 문제 해결의 핵심은 다음과 같다.
    • 입력 받은 시작 시간(@from@)과 끝나는 시간(@to@)을 2차원 벡터(@times@)에 넣는다.
      • 끝나는 시간(@to@)을 기준으로 오름차순 정렬을 해줄 것이기 때문에 더 간편하게 정렬을 해주기 위해서 2차원 벡터에는 [끝나는 시간, 시작 시간] 순서대로 값을 넣어준다.
    • 끝나는 시간(@to@)을 기준으로 오름차순 정렬을 해준다. (@sort(times.begin(), times.end();@)
    • 첫 번째 회의의 끝나는 시간기준값으로 정한 후 (@next = times[0].first@), 두 번째 회의부터 N번째 회의까지의 시작 시간(@times[i].second@)의 값과 비교해나간다.
      • 시작 시간의 값이 기준값보다 크거나 같을 경우(@if (next <= times[i].second)@) 카운팅을 해준다. (@cnt++@)
        • 카운팅 값을 저장하는 변수(@cnt@)의 초깃값은 1로 설정해준다.
          • 기준값을 설정할 때 첫 번째 회의의 경우를 미리 세었고, 두 번째 회의부터 비교를 해나가기 때문이다.
        • 동시에 기준값을 회의의 끝나는 시간으로 업데이트해준다. (@next = times[i].first@)
int Solution(vector<pair<int, int>> v) {
    int next;
    sort(v.begin(), v.end());    // 끝나는 시간 기준 오름차순 정렬

    next = v[0].first;
    cnt = 1;
    for (auto i = 1; i < N; i++) {
        if (next <= v[i].second) {
            cnt++;
            next = v[i].first;
        }
    }

    return cnt;
}

 

예제를 통해 알고리즘 이해하기

  • @예제 입력 1@의 값들을 '끝나는 시간' 기준으로 오름차순 정렬을 해주면 다음과 같다.
회의 순서 끝나는 시간 시작 시간
1 4 1
2 5 3
3 6 0
4 7 5
5 8 3
6 9 5
7 10 6
8 11 8
9 12 8
10 13 2
11 14 12
  • 기준값(@next@)을 첫 번째 회의의 끝나는 시간(@4@)으로 잡는다. ([1, 4]) (cnt = 1)
  • 그 다음에 가능한 회의는 시작 시간기준값(@4@)보다 크거나 같아야 한다. 따라서 네 번째 회의(시작 시간 : @5@)를 선택할 수 있다.([1, 4], [5, 7]) (cnt = 2)
    • 기준값네 번째 회의의 끝나는 시간(@7@)으로 업데이트해준다.
  • 그 다음에 가능한 회의는 시작 시간 기준값(@7@)보다 크거나 같아야 하므로, 여덟 번째 회의(시작 시간 : @8@)를 선택할 수 있다.([1, 4], [5, 7], [8, 11]) (cnt = 3)
    • 기준값 여덟 번째 회의의 끝나는 시간(@11@)으로 업데이트해준다.
  • 그 다음에 가능한 회의는 시작 시간 기준값(@11@)보다 크거나 같아야 하므로, 열 한 번째 회의(시작 시간 : @12@)를 선택할 수 있다.([1, 4], [5, 7], [8, 11], [12, 14]) (cnt = 4)
    • 기준값 열 한 번째 회의의 끝나는 시간(@12@)으로 업데이트해준다.
  • 더 이상 비교할 것이 없으므로 과정을 종료한다. [1, 4], [5, 7], [8, 11], [12, 14] 구간을 선택할 수 있으며, 최종적으로 @cnt@ 값(@4@)이 정답이 된다.

 

처음에 접근했던 방식

  • 처음에 다음과 같이 2중 for 문을 이용하여 전 회의 구간을 비교하면서 카운팅 횟수의 최댓값을 구하는 방식으로 문제를 해결하려고 하였으나, 시간 초과 때문에 문제를 해결할 수 없었다.
int Solution(vector<pair<int, int>> v) {
    int next;
    sort(v.begin(), v.end());    // 끝나는 시간 기준 오름차순 정렬

    for (auto i = 0; i < N; i++) {
        next = v[i].first;
        cnt = 1;
        for (auto j = i + 1; j < N; j++) {
            if (next <= v[i].second) {
                cnt++;
                next = v[i].first;
            }
        }
        maxCnt = max(maxCnt, cnt);
    }
    
    return maxCnt;
}
  • 결국 for 문을 1개만 써서 문제를 해결할 수 있다는 것을 알게 되었고, 문제를 해결할 수 있었다.
    • 끝나는 시간을 기준으로 오름차순 정렬을 했기 때문에 굳이 2중 for 문을 쓰지 않아도 됐다.
    • 왜냐하면, 상위 회의 구간의 카운팅 횟수는 하위 회의 구간의 카운팅 횟수를 포함하므로 굳이 전체의 구간을 서로 비교할 필요가 없기 때문이다.
int Solution(vector<pair<int, int>> v) {
    int next;
    sort(v.begin(), v.end());    // 끝나는 시간 기준 오름차순 정렬

    next = v[0].first;
    cnt = 1;
    for (auto i = 1; i < N; i++) {
        if (next <= v[i].second) {
            cnt++;
            next = v[i].first;
        }
    }

    return cnt;
}

 

코드

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int N, from, to, cnt;
vector<pair<int, int>> times;

void Input() {
    cin >> N;

    for (int i = 0; i < N; i++) {
        cin >> from >> to;
        times.push_back({ to, from });    // [끝나는 시간, 시작 시간]
    }
}

int Solution(vector<pair<int, int>> v) {
    int next;
    sort(v.begin(), v.end());    // 끝나는 시간 기준 오름차순 정렬

    next = v[0].first;
    cnt = 1;
    for (auto i = 1; i < N; i++) {
        if (next <= v[i].second) {
            cnt++;
            next = v[i].first;
        }
    }

    return cnt;
}

void Output() {
    cout << Solution(times) << '\n';
}

void Solve() {
    Input();
    Output();
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    Solve();

    return 0;
}

 

채점 결과

 

참고

  • [단계별로 풀어보기] > [그리디 알고리즘]
  • 실버I
728x90
728x90

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ-10813][C++] 공 바꾸기  (0) 2023.02.23
[BOJ-10810][C++] 공 넣기  (0) 2023.02.23
[BOJ-1541][C++] 잃어버린 괄호  (0) 2023.02.20
[BOJ-11399][C++] ATM  (0) 2023.02.09
[BOJ-11047][C++] 동전 0  (2) 2023.02.06
[BOJ-2751][C++] 수 정렬하기 2  (0) 2023.02.04
[BOJ-25305][C++] 커트라인  (0) 2023.02.04
[BOJ-2587][C++] 대표값2  (0) 2023.02.04