문제
17472번: 다리 만들기 2
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.
www.acmicpc.net
입력
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.
출력
모든 섬을 연결하는 다리 길이의 최솟값을 출력한다. 모든 섬을 연결하는 것이 불가능하면 -1을 출력한다.
풀이
해당 문제는 DFS를 이용한 완전 탐색을 이용하여 풀이하였습니다.
첫번째로 1, 0으로 이루어진 맵에서 각 섬들을 구별하기 위해 섬에 넘버링을 해주었습니다.
DFS를 이용하여 간단하게 구현할 수 있습니다.
void dfs(int x, int y, int n) {
visited[x][y] = true;
map[x][y] = n;
for (int i = 0; i < 4; i++) {
int nextX = x + dirX[i];
int nextY = y + dirY[i];
if (IsInRange(nextX, nextY) && map[nextX][nextY] == 1 && visited[nextX][nextY] == false) {
dfs(nextX, nextY, n);
}
}
}
// 1. 섬에 번호 붙이기
int num = 1;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] == 1 && visited[i][j] == false) {
dfs(i, j, num++);
}
}
}
numOfIsland = num - 1;
두번째로 두 섬을 연결 가능한 다리를 모두 구해줍니다.
다리 방향은 상, 하, 좌, 우만 가능하기 때문에 간단히 구현할 수 있습니다.
인접한 곳이 바다라면 계속 다리를 이어주고 바다가 아니라면 아래 조건을 통해 다리 연결 가능 여부를 확인합니다.
(1) 맵 안에 있으면서
(2) 출발지가 아니면서
(3) 다리 길이가 2 이상
void makeBridge(int x, int y, int from) {
for (int i = 0; i < 4; i++) {
int curX = x;
int curY = y;
int cnt = 0;
while (true) {
int nextX = curX + dirX[i];
int nextY = curY + dirY[i];
if (!IsInRange(nextX, nextY)) break;
else if (map[nextX][nextY] == from) break;
else if (map[nextX][nextY] == 0) {
curX = nextX;
curY = nextY;
cnt++;
continue;
}
else if (cnt < 2) break;
int to = map[nextX][nextY];
edges[from][to] = min(cnt, edges[from][to]);
edges[to][from] = min(cnt, edges[to][from]);
break;
}
}
}
// 2. 섬 X에서 섬 Y를 연결하는 다리 만들기
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] != 0) {
makeBridge(i, j, map[i][j]);
}
}
}
세번째로 다리를 연결할 수 있는 모든 경우의 수를 고려해 답을 구합니다.
이 때 1번 다리를 연결하고 3번 다리를 연결하든, 3번 다리를 연결하고 1번 다리를 연결하든 결과는 동일하기 때문에
조합을 사용하여 풀이하였습니다.
조합 CASE(다리 갯수가 4개인 경우) 0000 // 아무 다리도 연결되어있지 않은 경우 0001 // 4번째 다리만 연결된 경우 0010 0011 // 3번째, 4번째 다리만 연결된 경우 ... 1111 // 모든 다리가 연결된 경우 |
전체 코드는 아래와 같습니다.
#include <iostream>
#include <vector>
using namespace std;
int N, M;
const int MAX = 120;
const int MAX_INT = 987654321;
int map[MAX][MAX];
int edges[8][8];
int dirX[4] = {-1, 0, 1, 0};
int dirY[4] = {0, 1, 0, -1};
bool visited[MAX][MAX];
bool dfs_visited[8];
int answer = MAX_INT;
int numOfIsland;
struct myData {
int from;
int to;
int cost;
};
vector<myData> edgeList;
int temp[15];
bool IsInRange(int x, int y) {
if (x >= 0 && x < N && y >= 0 && y < M) return true;
else return false;
}
void dfs(int x, int y, int n) {
visited[x][y] = true;
map[x][y] = n;
for (int i = 0; i < 4; i++) {
int nextX = x + dirX[i];
int nextY = y + dirY[i];
if (IsInRange(nextX, nextY) && map[nextX][nextY] == 1 && visited[nextX][nextY] == false) {
dfs(nextX, nextY, n);
}
}
}
int min(int a, int b) {
if (a < b) return a;
else return b;
}
void makeBridge(int x, int y, int from) {
for (int i = 0; i < 4; i++) {
int curX = x;
int curY = y;
int cnt = 0;
while (true) {
int nextX = curX + dirX[i];
int nextY = curY + dirY[i];
if (!IsInRange(nextX, nextY)) break;
else if (map[nextX][nextY] == from) break;
else if (map[nextX][nextY] == 0) {
curX = nextX;
curY = nextY;
cnt++;
continue;
}
else if (cnt < 2) break;
int to = map[nextX][nextY];
edges[from][to] = min(cnt, edges[from][to]);
edges[to][from] = min(cnt, edges[to][from]);
break;
}
}
}
void init() {
cin >> N >> M;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> map[i][j];
}
}
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
edges[i][j] = MAX_INT;
}
}
}
void dfs_check(int n, vector<int> myGraph[10]) {
dfs_visited[n] = true;
for (int i = 0; i < myGraph[n].size(); i++) {
int next = myGraph[n][i];
if (dfs_visited[next] == false) {
dfs_check(next, myGraph);
}
}
}
void dfs(int x) {
if (x >= edgeList.size()) {
// TODO
vector<int> myGraph[10];
int totalCost = 0;
for (int i = 0; i < edgeList.size(); i++) {
if (temp[i] == 1) {
int from = edgeList[i].from;
int to = edgeList[i].to;
totalCost += edgeList[i].cost;
myGraph[from].push_back(to);
myGraph[to].push_back(from);
}
}
for (int i = 1; i <= numOfIsland; i++) {
dfs_visited[i] = false;
}
dfs_check(1, myGraph);
for (int i = 1; i <= numOfIsland; i++) {
if (dfs_visited[i] == false) {
return;
}
}
if (totalCost < answer) answer = totalCost;
}
else {
for (int i = 0; i <= 1; i++) {
temp[x] = i;
dfs(x + 1);
temp[x] = 0;
}
}
}
int main() {
init();
// 1. 섬에 번호 붙이기
int num = 1;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] == 1 && visited[i][j] == false) {
dfs(i, j, num++);
}
}
}
numOfIsland = num - 1;
// 2. 섬 X에서 섬 Y를 연결하는 다리 만들기
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] != 0) {
makeBridge(i, j, map[i][j]);
}
}
}
// 3. 다리 갯수만큼 완전 탐색
for (int i = 1; i <= numOfIsland; i++) {
for (int j = i + 1; j <= numOfIsland; j++) {
if (edges[i][j] != MAX_INT) {
edgeList.push_back({ i, j, edges[i][j] });
}
}
}
dfs(0);
if (answer == MAX_INT) cout << -1;
else cout << answer;
return 0;
}
'Algorithm' 카테고리의 다른 글
[C++][백준] 마법사 상어와 토네이도(20057) (1) | 2021.04.15 |
---|---|
[C++][백준] 인싸들의 가위바위보(16986) (0) | 2021.04.15 |
[C++][백준] 다리 만들기(2146) (0) | 2021.03.23 |
[C++][백준] 문명(14868) (0) | 2021.03.23 |
[C++][백준] 빗물(14719) (0) | 2021.03.23 |