Programming

[BOJ] 24545 Y

minigb 2022. 4. 6. 02:11

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

24545번: Y

첫째 줄에 트리의 정점 개수를 의미하는 정수 $N$이 주어진다. ($2 \leq N \leq 100\,000$) 둘째 줄부터 $N-1$개 줄에 걸쳐 트리를 이루는 간선의 정보를 나타내는 두 정수 $u$, $v$가 주어진다. 이는 $u$번 정

www.acmicpc.net

리쓴 투 마 와....아


SUAPC에서 푼 문제 중 기록해두고 싶은 문제다.
대회 때 내가 1인분을 하는 데 기여해줬다.

대회 중에 더 이상 문제가 안 풀려서 한참 진전이 없었을 때 팀원들과 셋이 의논하면서 솔루션을 도출해냈다.
이야기하다가 내가 문득 트리의 지름이 떠올렸고, 트리의 지름에 있는 노드들을 보면서 리프 노드까지의 개수가 최대인 걸 구하면 되지 않을까? 라고 했다.
근데 반례가 있을 거 같았는데, 다른 두 분이 아니라고 맞다고 하셨고, 그때 기령이가 수식을 적어가면서 증명해줬다.

5시간 중에 그때 막 텐션이 올랐던 참이라 이걸 정말 잘 짤 수 있을 것 같아서 내가 맡겠다고 했다.
다 짜고 나서 이전에 제출했던 코드에서 찾은 반례 등을 넣어보니 다 맞게 나왔고, 제출했더니 다행히 한 번에 맞았다.
대회에서 플레 문제를 한 번에 맞다니.
운도 좋았던 게, 며칠 전에 다른 팀이랑 연습하면서 두 번째 트리의 지름 문제를 한 번 봤던 상태였다.

int GetCnt(int cur, int before, const vector<vector<int>>& adj) {
	int ret = 0;
	for (const int& next : adj[cur]) {
		if (next == before) {
			continue;
		}
		ret = max(ret, GetCnt(next, cur, adj));
	}
	return ret + 1;
}

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n; cin >> n;
	vector<vector<int>> adj(n + 1);
	for (int i = 0; i < n - 1; ++i) {
		int a, b; cin >> a >> b;
		adj[a].push_back(b);
		adj[b].push_back(a);
	}

	queue<int> que;
	vector<bool> visited(n + 1, false);
	que.push(1);
	visited[1] = true;
	int last_node = 1;
	while (!que.empty()) {
		int cur = que.front();
		que.pop();

		last_node = cur;
		for (const int& now : adj[cur]) {
			if (visited[now]) {
				continue;
			}
			visited[now] = true;
			last_node = now;
			que.push(now);
		}
	}

	visited.clear();
	visited.resize(n + 1, false);
	int start_node = last_node;
	que.push(start_node);
	visited[start_node] = true;
	vector<int> before(n + 1, -1);
	while (!que.empty()) {
		int cur = que.front();
		que.pop();

		last_node = cur;
		for (const int& now : adj[cur]) {
			if (visited[now]) {
				continue;
			}
			visited[now] = true;
			before[now] = cur;
			que.push(now);
		}
	}

	vector<int> route;
	vector<bool> on_route(n + 1, false);
	for (int cur = last_node; cur != -1; cur = before[cur]) {
		route.push_back(cur);
		on_route[cur] = true;
	}

	if (route.size() == n) {
		cout << 0 << kEndl;
		return 0;
	}

	int max_cnt = 0;
	for (const int& cur : route) {
		for (const int& next : adj[cur]) {
			if (on_route[next]) {
				continue;
			}
			max_cnt = max(max_cnt, GetCnt(next, cur, adj));			
		}
	}

	cout << route.size() + max_cnt << kEndl;
}

처음에는 여기서 특정 노드가 트리의 지름에 속하는지 확인할 때 route 벡터에 저장된 걸 정렬해서 이분탐색 하려고 했다.
……
가끔 이럴 때가 있다. 눈앞에 닥친 것만 보다가 이상한 짓을 하는.
다행히 금방 정신 차리고 flag로 저장해두면 된다는 걸 깨달았다.

그리고 이 문제를 풀 때의 분위기가, 팀 대회의 재미가 충분히 발현되었던 거 같은 느낌.
셋이 이야기하면서 솔루션 도출하고, 한 이야기가 나왔을 때 그것에 대한 반례나 증명에서 다른 팀원이 서포트 하고, 그러다 확신이 들면 한 명이 코드를 작성하고 (이때도 다른 팀원의 도움을 받을 수도 있고), 제출하기 전에 셋이서 만들어놓은 예제를 넣어보면서 한 번 더 확인하고.

재밌었던 문제로 오래 기억될 거 같다.