코딩뚠뚠

[백준문제풀이] 2146 다리 만들기 본문

알고리즘 문제풀이/백준문제풀이

[백준문제풀이] 2146 다리 만들기

로디네로 2021. 1. 2. 10:45
반응형

 

풀이일시 : 2020-11-05

 

문제 :

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다는 생각을 하게 되었다. 그래서 그는, 생색내는 식으로 한 섬과 다른 섬을 잇는 다리 하나만을 만들기로 하였고, 그 또한 다리를 가장 짧게 하여 돈을 아끼려 하였다.

이 나라는 N×N크기의 이차원 평면상에 존재한다. 이 나라는 여러 섬으로 이루어져 있으며, 섬이란 동서남북으로 육지가 붙어있는 덩어리를 말한다. 다음은 세 개의 섬으로 이루어진 나라의 지도이다.

위의 그림에서 색이 있는 부분이 육지이고, 색이 없는 부분이 바다이다. 이 바다에 가장 짧은 다리를 놓아 두 대륙을 연결하고자 한다. 가장 짧은 다리란, 다리가 격자에서 차지하는 칸의 수가 가장 작은 다리를 말한다. 다음 그림에서 두 대륙을 연결하는 다리를 볼 수 있다.

물론 위의 방법 외에도 다리를 놓는 방법이 여러 가지 있으나, 위의 경우가 놓는 다리의 길이가 3으로 가장 짧다(물론 길이가 3인 다른 다리를 놓을 수 있는 방법도 몇 가지 있다).

지도가 주어질 때, 가장 짧은 다리 하나를 놓아 두 대륙을 연결하는 방법을 찾으시오.

입력 :

첫 줄에는 지도의 크기 N(100이하의 자연수)가 주어진다. 그 다음 N줄에는 N개의 숫자가 빈칸을 사이에 두고 주어지며, 0은 바다, 1은 육지를 나타낸다. 항상 두 개 이상의 섬이 있는 데이터만 입력으로 주어진다.

ex)

10

1 1 1 0 0 0 0 1 1 1

1 1 1 1 0 0 0 0 1 1

1 0 1 1 0 0 0 0 1 1

0 0 1 1 1 0 0 0 0 1

0 0 0 1 0 0 0 0 0 1

0 0 0 0 0 0 0 0 0 1

0 0 0 0 0 0 0 0 0 0

0 0 0 0 1 1 0 0 0 0

0 0 0 0 1 1 1 0 0 0

0 0 0 0 0 0 0 0 0 0

출력 :

첫째 줄에 가장 짧은 다리의 길이를 출력한다.

ex)

3

풀이 :

우선 육지의 개념 즉 이어져있다는 개념이 무엇인지 살펴보면 동,서,남,북으로 이어져 있어야 한다고 한다. 그러므로 대각선으로 이어져 있는 것은 이어져있는것이 아니다.

각 육지를 바다쪽으로 간척해나가며 문제를 풀어보자.

-> DFS로 각 섬마다 번호를 마킹해주도록 하자

-> BFS로 섬의 부피를 1씩 늘이고, 닿는다면 그때의 수를 센다.

( bfs로 섬의 부피를 늘릴때 1,2,3 섬을 모두 같이 늘리면 닿는 것을 판별하기 애매할 수 있다. 따라서 DFS로 설정해 준 섬의 번호를 돌아가며 1,2,3섬을 차례로 bfs 해준다. 그리고 그들의 min을 result로 삼는다.)

 

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>

#define MAX 100
using namespace std;

int graph[MAX][MAX];
int dx[] = { 0,0,1,-1 }; //상하좌우 탐색위해
int dy[] = { 1,-1,0,0 };
int visited[MAX][MAX];
int N;
int a;

int bfs(int cnt) { //bfs 돌린다
	queue <pair<int,int>> q; 
	//전체를 돌며 cnt 섬만 q에 넣을거다.
	for (int i = 0; i < N; i++) { 
		for (int j = 0; j < N; j++) {
			if (graph[i][j] == cnt) { //한 섬의 번호
				visited[i][j] = true; //방문 true
				q.push({ i,j }); //큐에 넣기
			}
		}
	}
	int result = 0;
	while (!q.empty()) {  //큐가 빌때까지 반복한다.
		int size = q.size(); //q의 사이즈 즉 초기에는 섬의 크기
		for (int i = 0; i < size; i++) { //사이즈만큼 반복. 전체의 부피를 4방향으로 늘릴것
			//이게 size만큼 반복되면 한 겹(result)이 쌓인 것 그다음 q는 그 쌓인 부피를 또 늘릴것이다.
			int y = q.front().first;
			int x = q.front().second;
			q.pop(); //pop해준다.
			for (int i = 0; i < 4; i++) { //4방향 탐색하기
				int ny = y + dy[i];
				int nx = x + dx[i];
				if (0 <= ny && ny < N && 0 <= nx && nx < N) { //범위 조건을 만족하고
					if (graph[ny][nx] && graph[ny][nx] != cnt) { //다른섬과 맞닿으면
						return result; //result를 return한다.
					}
					else if (!graph[ny][nx] && !visited[ny][nx]) //graph가 0이고(섬이아니고) visited하지 않았다면
					{
						visited[ny][nx] = true; //방문해주고
						q.push({ ny,nx }); //push 해준다.
					}
				}
			}
		}
		result++;
	}
}

void dfs(int y, int x, int cnt) { //dfs를 통해 각 섬의 번호를 만들어준다.
	visited[y][x] = true; //지금 들어온 번호 true로 만들어주고 시작
	graph[y][x] = cnt; //섬의 번호를 cnt로 만들어준다.
	for (int i = 0; i < 4; i++) { // 4번반복
		int ny = y + dy[i]; //번호매겨준다.
		int nx = x + dx[i];
		if (0 <= ny && ny < N && 0 <= nx && nx < N) { //범위를 충족하면
			if (graph[ny][nx] && !visited[ny][nx]) { //땅이면(0이 아니여야됨) && 방문하지않았다면
				dfs(ny, nx, cnt); //다시 dfs 넣어서 점령한다.
			}
		}
	}
}

int main() {
	cin >> N;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cin >> a;
			graph[i][j] = a;
		}
	}
	//각 섬을 다른수로 마킹해주기 위한 dfs
	int cnt = 1;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (graph[i][j] && !visited[i][j])
				dfs(i, j, cnt++); //다끝나고 나오면 또 cnt증가시켜서 다른곳들어감
		}
	}

	int result = 100000;
	//result min의 의미 : 한 번 bfs를 실행하면 1 섬에서 2, 3 섬으로 가는 다리 중 최소를 얻음
	// 2 섬에서의 1 3 까지의 result와 3 섬에서 2 1 까지의 최소랑 비교하여 min을 찾기위함
	for (int i = 1; i < cnt; i++) {
		memset(visited, 0, sizeof(visited));
		result = min(result, bfs(i));
	}
	cout << result << endl;
	return 0;
}

 

참고자료 : BFS

dbstndi6316.tistory.com/63

 

[기본문제풀이] BFS 너비우선탐색

풀이 일시 : 2020-08-30 ​ BFS : 탐색 - 하나의 정점으로부터 시작하여 차례대로 모든 정점들을 한번씩 방문하는 것. 그 중 한 방법인 너비우선탐색은 루트 노드에서 시작해서 인접한 노드를 먼저 탐

dbstndi6316.tistory.com

참고자료 : DFS

dbstndi6316.tistory.com/64

 

[기본문제풀이] DFS 깊이우선탐색

풀이일시 : 2020-08-30 ​ DFS : 깊이우선탐색으로 DFS보다 좁고 깊게 탐색해나가며 전체 정점을 탐색하는 방법이다. 주로 stack을 이용한다. 아래그림이 dfs를 한눈에 보여준다고 생각한다 출처 : http://

dbstndi6316.tistory.com

참고자료 : memset 함수

dbstndi6316.tistory.com/28

 

[개념정리] memset함수

메모리를 조작하는 함수로는 대표적으로 memset, memcpy, memmove, memcmp 등이 있다. 그 중 메모리를 초기화하는 memset을 알아본다. ​ memset함수란 메모리의 내용을 원하는 크기만큼 특정 값으로 세팅할

dbstndi6316.tistory.com

 

반응형