Posts [백준] 2887 - 행성 터널
Post
Cancel

[백준] 2887 - 행성 터널


[백준] 2887 - 행성 터널

[링크] https://www.acmicpc.net/problem/2887


💎 문제 조건과 설명

문제 조건과 설명은 위의 링크를 참조 부탁드립니다.


🚀 생각한 아이디어

이 문제는 크루스칼 알고리즘을 구현하는 문제입니다.

크루스칼 알고리즘은 각 정점 간 간선의 가중치를 작은 순서대로 선택해 나가는데, union-find 연산으로 싸이클이 발생하는지 여부를 판단하면서 선택하는 것이 주요 구현 내용입니다.

여기서 선택의 대상인 간선의 가중치 값은, 문제에서 주어진 각 행성들 간의 X, Y, Z 좌표 차이 중 최솟값 이 됩니다. 그러므로 문제에서 간선의 가중치가 될 수 있는 이 값을 찾아놓는 것이 핵심입니다.

처음에는 모든 행성들 간에 서로 X, Y, Z 좌표값 차이를 구해서 간선의 가중치 정보로 넣는 방법을 생각할 수 있지만, 이 문제에서는 행성이 최대 10만개 주어지므로 대략 10만*10만 만큼의 비교 및 데이터가 발생합니다. 따라서 메모리 초과 문제가 발생할 수 있습니다.

어차피 행성들 간의 X, Y, Z 좌표값 차이 중 가장 작은 값들이 간선 가중치 후보가 될 수 있는 것이므로 아래와 같은 방법을 생각할 수 있습니다.

  • 각 행성들의 X,Y,Z 좌표를 별도로 벡터에 담아두고 오름차순 정렬해 놓는다.
  • X 좌표를 우선 예로 들면, 행성 A. B, C, D 의 X좌표가 각각 11, 3, 2, 8 이었을 경우 오름차순 정렬하면 2, 3, 8, 11 이며, 각각 C, B, D, A 순이다.
  • 따라서 행성 C-B, B-D, D-A 로 연결될 때 각각의 X좌표 차이(3-2, 8-3, 11-8)가 행성들 간 X좌표 차이의 최솟값이 된다.
  • 즉, 행성 C는 B와 연결될 때 X좌표 차이가 가장 작으며, 행성 B는 행성 D와 연결될 때 X좌표 차이가 가장 작다.
  • Y, Z 좌표에도 위와 똑같은 방법을 적용한다. 그리고 구해진 X, Y, Z 좌표 차이의 최솟값들 모음을 가지고 크루스칼 알고리즘을 진행한다.

🚀 소스코드(C++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <algorithm>
#include <string>
#include <stack>
#include <vector>
#include <queue>
#include <map>

#define pii pair<int, int>

using namespace std;

vector<pair<int, int>> X;
vector<pair<int, int>> Y;
vector<pair<int, int>> Z;

vector<pair<int, pii>> info;
int parent[100001];

int find(int x) {
	if (parent[x] == x) return x;
	return parent[x] = find(parent[x]);
}

void unite(int a, int b) {
	a = find(a);
	b = find(b);

	if (a < b) parent[b] = a;
	else parent[a] = b;
}

int main() {

	ios::sync_with_stdio(0);
	cin.tie(0);

	int N;
	cin >> N;

	for (int i = 0; i < N; i++) {
		parent[i] = i;
	}

	for (int i = 0; i < N; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		X.push_back({ x,i });
		Y.push_back({ y,i });
		Z.push_back({ z,i });
	}

	sort(X.begin(), X.end());
	sort(Y.begin(), Y.end());
	sort(Z.begin(), Z.end());

	for (int i = 0; i < N - 1; i++) {
		info.push_back({ X[i + 1].first - X[i].first , {X[i].second, X[i + 1].second} });
		info.push_back({ Y[i + 1].first - Y[i].first , {Y[i].second, Y[i + 1].second} });
		info.push_back({ Z[i + 1].first - Z[i].first , {Y[i].second, Y[i + 1].second} });
	}

	sort(info.begin(), info.end());

	int cnt = 0;
	int total = 0;

	for (int i = 0; i < info.size(); i++) {
		int a = info[i].second.first;
		int b = info[i].second.second;
		int val = info[i].first;

		if (find(a) != find(b)) {
			unite(a, b);
			total += val;
		}
	}

	cout << total;

	return 0;
}

[LG글로벌챌린저] (2) 면접을 위해 다시 새로운 도전..그리고 합격!

[GitHub] git commit 템플릿 사용하여 commit message convention 준수하기