[백준] 1931 회의실 배정 - 그리디

jckim22·2023년 6월 27일
0

[ALGORITHM] STUDY (PS)

목록 보기
6/86

문제

1931번 문제 바로가기

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

입력

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

출력

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

예제 입력

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

예제 출력

4

문제 검토

회의실의 끝나는 시간이 가장 빠르고 그 다음 회의 시작 시간이 이전 회의 시간의 끝나는 시간보다 크면서 가장 가까워야 하고 끝나는 시간 또한 앞선 조건을 만족한다는 전제하에 가장 가깝게 커야한다.
이 2가지 조건이 충족되는 회의 시간을 채택한다.

매 수행마다 가장 빠르고 가장 가까운 시간만을 찾을 때 최적의 해를 구한다는 점에서 그리디 기법을 사용하는 것이 알맞다.

풀이(python, cpp)

Python

from sys import stdin

N = int(stdin.readline())
S=list()
answer=1 #첫 회의 카운트

for x in range(N): # 회의 입력 받음(2차원 리스트)
    a,b=map(int,stdin.readline().split())
    l=[a,b]
    S.append(l)
    
S=sorted(S,key=lambda y:(y[1],y[0])) # 끝나는 시간을 첫번째 기준, 시작시간을 두번째 기준으로 하여 정렬

curend = S[0][1] #현재 회의의 끝나는 시간을 담음

for x in range(1,len(S)): #현재 회의를 제외하고 반복 시작

    if S[x][0]>=curend: #현재회의의 끝나는 시간보다 시작시간이 크다면
        curend=S[x][1] #그 회의의 끝나는 시간을 현재 회의의 끝나는 시간으로 변경
        answer+=1 #회의 카운트
        
print(answer)
        

람다식을 이용하여 정렬 기준을 2가지로 잡아준다.
코드 해석은 주석에 달아놓았다.

Cpp

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
bool compare(pair<int, int> a, pair<int, int> b) {
	if (a.second < b.second) {
		if(a.first)
		return true;
	}
	return false;
}
int main() {
	int N,tmp,tmpf,cnt=1;
	vector<pair<int, int>> v;

	scanf("%d", &N);

	int* start = new int[N];
	int* end = new int[N];

	for (int i = 0; i < N; i++) {
		scanf("%d %d", &start[i], &end[i]);
	}

	for (int i = 0; i < N; i++) {
		v.push_back(pair<int, int>(start[i], end[i]));
	}

	sort(v.begin(), v.end(),compare);

	tmp = v[0].second;
	tmpf = v[0].first;

	for (int i = 1; i < N; i++) {
		if (tmp == v[i].second && tmpf > v[i].first) {
			v[i].first = tmpf;
		}
		if (tmp <= v[i].first) {
			cnt++;
			tmp = v[i].second;
			tmpf = v[i].first;
		}
	}
	printf("%d", cnt);	
}

operator를 사용하여 정렬 기준을 잡아주었다.
파이썬의 2차원 리스트 대신 Cpp로는 vector를 사용해주었다.

총평

예제 입력에 끝나는 시간 기준 오름차순에 대한 힌트가 없다면 실버 1보다는 조금 더 어려울 수도 있겠다.

c++ 풀이는 2년전에 부족한 실력으로 하루종일 구글링하며 풀었던 코드이다.

문제가 기억나지 않았고 그리디 기법에 대한 이해가 부족했어서 이번에 python으로 다시 풀었는데 2년전 보다는 훨씬 가볍게 접근할 수 있었다.


위가 파이썬이고 밑이 cpp로 풀었을 경우이다.

확실히 메모리나 시간 쪽에서 차이가 많이 난다.

하지만 역시 python은 c++처럼 따로 operator를 정의해주지 않아도 되고 vector보다 훨씬 문법이 편리한 리스트를 사용하면 충분히 2차원 리스트를 스택구조로 입력 받을 수 있기 때문에 문법적으로 편했다.

그리디 기법이 용이한지 파악하는 것이 중요한 문제였다.

profile
개발/보안

0개의 댓글