백준 17472 - 다리 만들기 2
풀이
모든 섬을 연결하기 위해 필요한 다리 길이의 최솟값을 구해야 한다.
-
각 섬이 동일한 섬인지 판별하기 위해서 상하좌우로 인접한 섬끼리는 넘버링(numbering)을 수행하였고, 이 과정에서 DFS를 활용하였다. (BFS로 해도 무방하다.)
(시작하는 섬의 번호, 도착하는 섬의 번호, 두 섬을 잇는데 필요한 다리 길이)
의 정보를Node
구조체(src, dest, len)
으로 표현하였고, 각 섬을 최소 길이로 이어야 하므로 우선순위 큐 라이브러리에서 값을 꺼내올 때 len 값이 가장 작은 값부터 접근할 수 있도록bool operator<
연산자를 오버로딩하였다. -
모든 섬의 임의의 위치(값이 1인 위치)에서 바다 위치(값이 0인 위치)로 일직선으로 나아가면서 섬에 도달할 때까지 반복한다. 이때, 시작 위치의 섬 번호와 도착 위치의 섬 번호가 다르면서 다리 길이가 2이상을 만족하면 이 값을 큐에 저장한다. 이 과정은 상하좌우 4방향에 대해 반복된다.
-
정점의 개수가 N개(이 문제에서는
landCnt
개)인 그래프에서 MST를 이루기 위한 최소 간선의 개수는 (N-1)이다. 따라서 임의의 두 섬을 연결한 결과(merge
)가true
이면pickCnt
값을 1씩 증가시키고,pickCnt
값이섬의 개수(landCnt) - 1
을 만족하면 MST가 정상적으로 형성된 것이다.
- MST는 Union-Find 알고리즘을 사용한다.
- 유니온 파인드 : 백준 1717 - 집합의 표현
소스 코드
// since C++11
// 19 minutes
#include <iostream>
#include <queue>
using namespace std;
const int dr[] = { -1,1,0,0 };
const int dc[] = { 0,0,-1,1 };
struct Node {
int src;
int dest;
int len;
bool operator<(const Node& other) const {
return len > other.len;
}
};
priority_queue<Node> pq;
int N, M;
int landCnt; // land count
int field[10][10];
int ans;
int number[10][10]; // land number
// MST
int parent[7]; // max land conut : 6
void initMST() {
for (int i = 1; i <= landCnt; ++i) {
parent[i] = i;
}
}
int find(int u) {
if (parent[u] == u) {
return u;
}
return parent[u] = find(parent[u]);
}
bool merge(int u, int v) {
u = find(u);
v = find(v);
if (u == v)
return false;
parent[u] = v;
return true;
}
inline static bool isRange(const int& r, const int& c) {
return 0 <= r && r < N && 0 <= c && c < M;
}
void dfs(int r, int c) {
number[r][c] = landCnt;
for (int d = 0; d < 4; ++d) {
int nr = r + dr[d];
int nc = c + dc[d];
if (!isRange(nr, nc) || field[nr][nc] == 0 || number[nr][nc] != 0) continue;
dfs(nr, nc);
}
}
void landNumbering() {
for (int r = 0; r < N; ++r) {
for (int c = 0; c < M; ++c) {
if (field[r][c] == 1 && number[r][c] == 0) {
++landCnt;
dfs(r, c);
}
}
}
}
void go(int r, int c) {
for (int d = 0; d < 4; ++d) {
int nr = r;
int nc = c;
int len = 0;
while (true) {
nr += dr[d];
nc += dc[d];
if (!isRange(nr, nc)) break;
if (field[nr][nc] == 0) { // sea
++len;
}
else { // land
// startPoint != endPoint --> valid position
// startPoint = number[r][c]
// endPoint = number[nr][nc]
if (number[r][c] != number[nr][nc] && len >= 2) {
pq.push({ number[r][c], number[nr][nc], len });
}
break;
}
}
}
}
void goBridge() {
for (int r = 0; r < N; ++r) {
for (int c = 0; c < M; ++c) {
if (field[r][c] == 1) {
go(r, c);
}
}
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> N >> M;
for (int r = 0; r < N; ++r) {
for (int c = 0; c < M; ++c) {
cin >> field[r][c];
}
}
landNumbering();
goBridge();
initMST();
int pickCnt = 0;
// MST condition == link count : (landCnt - 1)
while (!pq.empty()) {
const Node now = pq.top();
pq.pop();
if (merge(now.src, now.dest)) {
ans += now.len;
++pickCnt;
if (pickCnt == landCnt - 1) {
break;
}
}
}
if (pickCnt == landCnt - 1) {
cout << ans;
}
else {
cout << -1;
}
return 0;
}