굥뷰를 햡시댜

[BOJ - 17472] 다리 만들기2 본문

알고리즘 문제 풀이/BOJ 문제 풀이

[BOJ - 17472] 다리 만들기2

GodZ 2019. 9. 30. 21:06

https://www.acmicpc.net/problem/17472

 

17472번: 다리 만들기 2

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

www.acmicpc.net

 

 

이번 9월 삼성 A형 역량테스트 복기된 문제로 난이도는 이제껏 풀었던 A형 문제중 어려운 문제에 속한것 같다.

 

지난번 게리맨더링 문제도 난이도가 꽤 높았고 이번 문제도 꽤 높은걸 보니

 

이제는 출제자 측에서도 지원자들이 상향평준화 되었다는걸 알아서 올해 하반기 지원자들의 수준을 테스트해보기 위해 삼성 역량테스트 전에 문제의 난이도를 조금 올린듯 하다.

 

이번 문제를 풀 때는 BFS와 크루스칼 알고리즘을 사용했다.

 

- 풀이 방법

1. 입력을 받는다.

 

2. BFS로 떨어진 섬들에게 번호를 부여한다.

(번호를 부여하면서 섬의 좌표들을 vector 배열에 수집한다.) -> 섬의 갯수가 최대 6개이기 때문에 시간 초과 안남

 

3. vector배열에 수집된 좌표들을 이용해 섬에서 섬까지의 최단 경로를 구한다(직선으로)

- 저는 재귀로 구했습니다.

- dist라는 이차원 배열에 저장했는데 dist[i][j]는 i섬에서 j섬까지의 최단 거리 입니다.

 

4. 최소 스패닝 트리를 만들어준다.

- 크루스칼 알고리즘을 사용합니다.

- union-find를 알아야 하기 때문에 모르신다면 구글 검색을 추천...

 

5. 모든 섬들이 연결이 되어있는지 find_parent함수를 이용해 확인합니다.

 

6. 답을 출력합니다.

 

 

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

struct POINT {
	int y, x;
};
struct DIST {
	int from, to, dist;
};
int n, m, ans;
int map[11][11];
int map_number[11][11];
int visited[11][11];
int dist[7][7];
int parent[7];
vector<POINT> area[7];
vector<DIST> line;
const int dy[] = { -1,1,0,0 };
const int dx[] = { 0,0,-1,1 };

bool cmp(DIST a, DIST b) {
	return a.dist < b.dist;
}

int find_parent(int a) {
	if (parent[a] == a) return a;
	else return parent[a] = find_parent(parent[a]);
}

void parent_union(int a, int b) {
	int aRoot = find_parent(a);
	int bRoot = find_parent(b);
	parent[aRoot] = bRoot;
}

void find_dist(int start, int u, int v, int dir, int val, int dist_cnt) {

	int ny = u + dy[dir];
	int nx = v + dx[dir];

	if (ny < 0 || ny >= n || nx < 0 || nx >= m) return;
	if (map_number[ny][nx] == 0) {
		find_dist(start, ny, nx, dir, val, dist_cnt + 1);
	}
	else if (map_number[ny][nx] != val && dist_cnt >= 2) {
		dist[start][map_number[ny][nx]] = min(dist[start][map_number[ny][nx]], dist_cnt);
		return;
	}
	else return;
}

int main(void) {

	scanf("%d %d", &n, &m);

	for (int y = 0; y < n; y++) {
		for (int x = 0; x < m; x++) {
			scanf("%d", &map[y][x]);
		}
	}

	int cnt = 0;
	//섬에 넘버링 해줌
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < m; x++) {
			if (map[y][x] != 0 && visited[y][x] == 0) {
				cnt += 1;

				queue<POINT> q;
				q.push({ y,x });
				visited[y][x] = 1;
				map_number[y][x] = cnt;
				area[cnt].push_back({ y,x });

				while (!q.empty()) {
					POINT cur = q.front();
					q.pop();

					for (int dir = 0; dir < 4; dir++) {
						POINT next;
						next.y = cur.y + dy[dir];
						next.x = cur.x + dx[dir];

						if (next.y < 0 || next.y >= n || next.x < 0 || next.x >= m || visited[next.y][next.x]) continue;
						if (map[next.y][next.x] == 0 || map[next.y][next.x] != map[cur.y][cur.x]) continue;

						visited[next.y][next.x] = 1;
						map_number[next.y][next.x] = cnt;
						area[cnt].push_back(next);
						q.push(next);
					}
				}
			}
		}
	}

	for (int y = 0; y < 7; y++) {
		for (int x = 0; x < 7; x++) {
			dist[y][x] = 0x7fffffff;
		}
	}

	//섬에서 섬까지 연결될 수 있는 모든 경로 찾기
	for (int i = 1; i <= cnt; i++) {
		for (int j = 0; j < area[i].size(); j++) {
			int y = area[i][j].y;
			int x = area[i][j].x;

			for (int dir = 0; dir < 4; dir++) {
				find_dist(i, y, x, dir, map_number[y][x], 0);
			}
		}
	}

	//간선의 정보를 모음
	for (int i = 1; i <= cnt; i++) {
		for (int j = 1; j <= cnt; j++) {
			if (dist[i][j] != 0x7fffffff) {
				line.push_back({ i,j,dist[i][j] });
			}
		}
	}
	//간선 작은 순으로 정렬
	sort(line.begin(), line.end(), cmp);

	//부모 초기화
	for (int i = 1; i <= cnt; i++) parent[i] = i;

	//최소 스패닝 트리를 만들어줌
	for (int i = 0; i < line.size(); i++) {
		int from = line[i].from;
		int to = line[i].to;

		int pfrom = find_parent(from);
		int pto = find_parent(to);

		if (pfrom != pto) {
			parent_union(pfrom, pto);
			ans += line[i].dist;
		}
	}
	//연결되었는지 검사
	int temp1 = find_parent(1);
	for (int i = 2; i <= cnt; i++) {
		int temp2 = find_parent(i);
		if (temp1 != temp2) {
			ans = -1;
			break;
		}
	}

	if (ans == 0) ans = -1;
	printf("%d\n", ans);

	getchar();
	getchar();

	return 0;
}

 

'알고리즘 문제 풀이 > BOJ 문제 풀이' 카테고리의 다른 글

[BOJ - 5427] 불  (0) 2019.10.03
[BOJ - 2234] 성곽  (0) 2019.09.30
[BOJ-1940] 주몽  (0) 2019.09.18
[BOJ - 16637] 괄호 추가하기  (0) 2019.08.23
[BOJ - 17406] 배열 돌리기 4  (0) 2019.08.17
Comments