16 Jul 2020

[BOJ 10775]

공항

문제 바로가기 : https://www.acmicpc.net/problem/10775

승원이는 좋겠다. 선물이 공항이라니

Solution


review

G개의 게이트, P개의 비행기가 있을 때 1번부터 g_i(1<=g_i<=G)번 게이트중 하나에 도킹한다고 설명이 돼있는데,
최대한 많은 비행기가 도킹되려면 g_i부터 내림차순으로 채워가야 한다. 1개씩 내려가며 체크하면 효율성이 떨어진다.
때문에 union-find를 통해서 parent에 바로 접근하면서 시간초과를 잡아야 한다!
여태까지는 임의로 집합을 만들어주고 부모를 찾아갔는데, 이 문제는 그렇지 않다는게 포인트다.

먼저 기존의 union-find 방식처럼 기본값을 본인 위치로 지정해주자.

for (int i = 0; i <= G; i++)
		uni[i] = i;

도킹하려는 위치에 이미 비행기가 있다면 중단해야 하므로 close를 만들어줬고, 조건은 부모값이 0일 때다. (???)
부모값이라 표현해서 혼란스러울 수 있는데 편의상 uni[num]을 부모값이라 하자.

int close = 0;
while (P--) {
  cin >> num;
  if (close) continue;
  if (find_parent(num) == 0) {
    close = 1;
    continue;
  }
  process(num);
}

왜 부모값이 0일 때로 했을까?
union-find 함수에서는 다음과 같이 uni[num]=num 일 때 num을 반환한다.

//부모 노드 반환
int find_parent(int a) {
	if (uni[a] == a) {
		return a;
	}
	return uni[a] = find_parent(uni[a]);
}

때문에 최종적으로 반환하는 위치는 uni[num]=num 일 때인데, 이렇게 되면 초기값에서 벗어날 수가 없다.
그래서 process 함수 안에 bool chk[]와 make_union(num, num - 1)을 넣었다.
체크가 안됐으면 바로 앞 위치(num-1)의 부모값을 참조해 같은 union으로 만들어준다.
체크가 됐을땐 조건(find_parent(num) == 0)에 걸리면 중단, 아니면 재귀로 진행한다.
0번 게이트는 없으면서 마침 uni[0]=0이므로 0과 union이 되는 범위는 비행기가 들어갈 수 없다!

void process(int num) {
	if (!chk[num]) {
		chk[num] = true;
		make_union(num, num - 1);
		ans++;
	}
	else {
		if (find_parent(num) == 0) return;
		process(find_parent(num));
	}
}

첫 번째 예제) G:4 P:3 g_i:[4, 1, 1]
index | 0 1 2 3 4
uni[i]| 0 0 2 3 3

uni[1]=0인데 g_i=1이 들어오므로 close => ans:2


항상 부모값을 참조할 땐 uni[num]말고 find_parent(num)을 사용하자! 삽질좀 그만하자!

Location

Icheon, KR

Email

iteratively@naver.com

Social

-->