Algorithm

[백준 1647] 도시 분할 계획

Mirab 2021. 11. 18. 22:48
문제 풀이

 

문제를 요약하면 다음과 같다.

1. 마을을 연결해야 된다. 그런데 가능한 최소의 비용으로 만들고 싶다.

2. 그런데 마을을 하나의 마을로 연결하는 게 아니라 나는 두 마을로 쪼개고 싶다. 그래도 가능한 최소의 비용으로 하고 싶다.

 

마을을 하나의 최소한의 비용으로 연결하고 싶었다면 MST 알고리즘인 크루스칼 알고리즘을 사용하면 된다.

그런데 이 문제는 마을을 한 개가 아니고 두 개로 나누고 싶어하기 때문에 고민이 필요하다.

 

신기한 점은 MST를 이룰 때 노드의 개수가 N개면 최소한의 비용으로 연결하려면 간선의 개수는 N - 1개만 필요하게 된다. 예를 들어 노드 5개가 있을 때 4개의 간선만으로도 5개의 노드를 모두 연결할 수 있기 때문이다.

 

그러면 마을이 2개라면?

노드의 개수가 N개고, 간선의 개수는 N - 2개면 충분하다.

간선의 개수 N - 1개 까지는 마을 A에 연결시켜주고 마지막 간선만 딱 때어버리면 남겨진 마을이 곧 마을 B이기 때문이다.

 

가벼운 문제지만 살짝 깊이있는 질문을 하는 문제였다.

 

소스 코드
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

const int MAX = 100001;
int N, M;
vector<pair<int, pair<int, int> > > edges;
int parent[MAX];
int findParent(int x)
{
	if (x == parent[x]) return x;
	else return parent[x] = findParent(parent[x]);
}

void unionParent(int a, int b)
{
	a = findParent(a);
	b = findParent(b);
	if (a < b) parent[b] = a;
	else parent[a] = b;
}

void input()
{
	cin >> N >> M;
	for (int i = 0; i < M; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		edges.push_back({ c, {a,b} });
	}

	sort(edges.begin(), edges.end());
	for (int i = 1; i <= N; i++) parent[i] = i;
}

void solve()
{
	input();

	int ans = 0;
	int cnt = 0; // 간선 포함 개수
	for (int i = 0; i < edges.size(); i++)
	{
		int cost = edges[i].first;
		int a = edges[i].second.first;
		int b = edges[i].second.second;
		if (findParent(a) != findParent(b))
		{
			unionParent(a, b);
			ans += cost;
			cnt++;
			// 마지막 간선을 제외한다.
			if (cnt == N - 2)
				break;
		}
	}

	cout << ans << endl;
}

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}