각 노드를 최소 비용으로 연결시키는 문제이다. 최소 신장 트리를 만드는 Kruskal 알고리즘을 사용하면 된다. 알고리즘은 간단하다. 우선 cost를 기준으로 오름차순 정렬시킨다.
i번 째 cost를 골라 두 섬이 연결되어 있는지 확인한다. 만약 연결되어 있다면(같은 그룹이라면) 다시 연결할 필요없다. 만약 연결되어 있지 않다면 (같은 그룹이 아니라면) 두 노드를 연결시키고(같은 그룹으로 표시) 해당 비용을 누적시킨다.
이를 모든 costs 요소에 대해 확인하면 된다.
여기서 핵심은 두 섬이 서로 연결되어 있는지, 같은 그룹에 있는지 확인하는 방법이다. 방법은 생각보다 간단한다. 우선 섬의 개수인 n의 크기를 가지는 int 타입 원소를 저장하는 1차원 배열을 하나 생성한다. 초기값은 모두 -1로 설정한다.
배열의 이름은 groupIDs이다. groupIDs[i]는 섬 i가 속한 그룹의 값이다. groupIDs[0] == groupIDs[2]이면 섬 0과 2는 서로 같은 그룹에 속해있는 것이다. 즉 연결되어 있다고 본다. 값 설정의 기준은 상관없다.
만약 현재 cost가 2이고 두 노드(섬)가 0, 2라면 두 노드의 그룹ID를 확인한다. 만약 두 값이 모두 -1이라면, 어디에도 연결되어 있지 않으므로 두 노드를 하나의 그룹으로 만든다. 여기서 그룹ID는 두 노드의 ID(0, 2)중 더 작은 값으로 설정한다. 즉 groupIDs[0] == groupIDs[2] == 0이 된다.
또 다른 경우로 하나의 노드는 그룹에 속해있고 하나의 노드는 그룹이 없을 수 있다. 그럼 그룹이 없는 노드를 다른 노드의 그룹으로 포함시킨다. 만약 노드 2와 3에 대해 groupIDs[2] == 0이고 groupIDs[3] == -1일 경우 groupIDs[3] = 0으로 만들어주면 된다.
또 다른 경우로 두 노드 모두 그룹이 있는 경우이다. 여기는 두 가지로 나뉜다. 같은 그룹에 속해 있거나 다른 그룹에 속해있거나.
같은 그룹에 속해 있다면 굳이 연결할 필요가 없으므로 스킵한다. 같은 그룹이 아니라면 두 그룹을 합쳐야 한다.(연결시켜야 한다.) 만약 groupIDs[3] == 0이고 groupIDs[6] == 6이라면, 노드 6을 노드 3 그룹에 포함시키던가, 그 반대로 해야한다. 아무거나 상관은 없지만 여기선 더 작은 그룹ID로 이동시킨다. 단순히 노드 6의 그룹ID만 변경하면 안되고, groupIDs를 돌면서 값이 6인 노드를 모두 0으로 변경해주어야 한다.
이를 반복할 때마다 cost를 누적시켜주면 정답이 된다.
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int solution(int n, vector<vector<int>> costs) {
// cost 기준 오름차순 정렬
sort(costs.begin(), costs.end(), [](const vector<int>& v1, const vector<int>& v2)
{
return v1[2] < v2[2];
});
// groupIDs[i] => 섬 i가 속해있는 그룹ID
vector<int> groupIDs(n, -1);
int minCost = 0;
for(int i = 0; i < costs.size(); ++i)
{
int id1 = costs[i][0];
int id2 = costs[i][1];
int cost = costs[i][2];
// 두 노드가 아직 어떤 그룹에도 속해있지 않다면
if(groupIDs[id1] == -1 && groupIDs[id2] == -1)
{
// 두 노드의 그룹 ID를 둘 중 더 작은 id로 설정
int newGroupID = min(id1, id2);
groupIDs[id1] = newGroupID;
groupIDs[id2] = newGroupID;
}
else if(groupIDs[id1] == -1 || groupIDs[id2] == -1)
{ // 두 노드 중 하나만 그룹에 속해 있는 경우
// 그룹에 속해 있지 않은 섬의 그룹ID를
// 다른 노드가 속해 있는 그룹ID로 설정
if(groupIDs[id1] == -1)
groupIDs[id1] = groupIDs[id2];
else
groupIDs[id2] = groupIDs[id1];
}
else // 두 노드 모두 (서로 다른) 그룹에 속해 있는 경우
{
// 두 노드가 같은 그룹이면 연결하지 않음
if(groupIDs[id1] == groupIDs[id2])
continue;
// groupIDs(id1) 과 groupIDs(id2) 중 더 작은 값으로 통일
// ex) groupIDs(id1): 3, groupIDs(id2): 5라면
// groupIDs(x) = 5인 모든 값을 3으로 변경
int maxGroupID = max(groupIDs[id1], groupIDs[id2]);
int minGroupID = min(groupIDs[id1], groupIDs[id2]);
for(int i = 0; i < groupIDs.size(); ++i)
{
if(groupIDs[i] == maxGroupID)
groupIDs[i] = minGroupID;
}
}
// cost 추가
minCost += cost;
}
return minCost;
}
'Algorithm > 프로그래머스' 카테고리의 다른 글
[연습문제] 최고의 집합 (0) | 2021.09.24 |
---|---|
[Level 3] 단속 카메라 (0) | 2021.09.21 |
[Level 2] 큰 수 만들기 (0) | 2021.09.20 |
[Level 2] 점프와 순간이동 | C++ (0) | 2020.09.23 |
[Level 2] 위장 - 해시 | C++ (0) | 2020.05.28 |
댓글