https://www.acmicpc.net/problem/21924
해당 문제는 최소 신장 트리(MST) 문제로, 주어진 모든 도로를 이을 때의 가중치 합에서 MST의 가중치 합(== 최소 가중치 합)을 빼면 금액이 얼마나 절약되는지 구할 수 있다. 다만 MST를 구할 수 없는 경우에는 -1을 출력해야하므로, 어떤 경우에 MST가 성립하지 않는지 조건을 찾아야 한다. 이 문제에서는 MST를 구하기 위해 프림 알고리즘을 사용했다.
1) 각 노드에 대해 연결되어 있는 다른 노드들의 번호와 가중치를 입력 받아 저장하는 edges
를 만들어 각 노드에 연결되어 있는 다른 노드의 (가중치 노드 번호)들을 저장한다.
ex. 1과 4가 3의 가중치로 연결 : edges[1].push_back(3, 4);
edges[4].push_back(3, 1);
2) 이 때 sum
에 모든 가중치를 합하여 모든 도로가 연결되었을 때의 가중치 합을 계산한다.
3) edges
벡터에 대해 프림 알고리즘을 적용한다.
pq
를 선언한다.i
를 선언하고 임의의 시작점으로 할 노드의 번호로 초기화한다. (해당 코드에서는 노드 1부터 시작) 그리고 현재까지 MST에 추가된 노드 개수를 저장하기 위한 count
를 선언하고 1로 초기화 한다. count
가 노드의 총 개수인 n
보다 작은 동안 아래 과정을 반복한다.sum
을 -1로, answer
를 0으로 초기화해 sum - answer
가 -1이 나오도록 하고, 곧바로 프림 알고리즘을 종료한다.answer
에 누적시키고, i
를 해당 간선에 연결된 상대 노드로 변경한다. 또한 count
를 하나 증가시킨다.4) 절약되는 금액인 sum - answer
(== 모든 도로 연결 금액 - 최소 금액)를 출력한다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>
#include <queue>
using namespace std;
typedef pair<int, int> pii;
vector<vector<pii>> edges;
bool mst[100001] ;
long long answer = 0, sum = 0;
void prim(int n)
{
priority_queue<pii, vector<pii>, greater<pii>> pq;
int i = 1, count = 1;
while (count < n)
{
mst[i] = true;
for (int j = 0; j < edges[i].size(); j++)
{
int curNode = edges[i][j].second;
if (!mst[curNode])
pq.push(edges[i][j]);
}
if (pq.empty()) // 현재 노드의 간선들을 확인한 직후 우선순위 큐에 집어넣을 간선이 없으면
{ // MST가 성립할 수 없는 경우이다.
sum = -1;
answer = 0;
return;
}
for (int k = 0; k < pq.size(); k++)
{
pii curEdge = pq.top();
pq.pop();
if (!mst[curEdge.second])
{
answer += curEdge.first;
i = curEdge.second;
count++;
break;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(); cout.tie();
int n, m;
cin >> n >> m;
edges.resize(n + 1);
for (int i = 0; i < m; i++)
{
int a, b, c;
cin >> a >> b >> c;
edges[a].push_back(pii(c, b));
edges[b].push_back(pii(c, a));
sum += c; // 모든 간선 이을 때 간선 가중치의 총합
}
prim(n);
cout << sum - answer;
}