난이도 : Gold 3
문제번호 : 17472
문제 주소 및 출처입니다.
https://www.acmicpc.net/problem/17472
목차
1. 문제 설명
2. 문제 해석
3. 소스 코드
1. 문제 설명
섬으로 이루어진 나라가 있고, 모든 섬을 다리로 연결하려고 한다. 이 나라의 지도는 N×M 크기의 이차원 격자로 나타낼 수 있고, 격자의 각 칸은 땅이거나 바다이다. 섬은 연결된 땅이 상하좌우로 붙어있는 덩어리를 말하고, 아래 그림은 네 개의 섬으로 이루어진 나라이다. 색칠되어있는 칸은 땅이다. 다리는 바다에만 건설할 수 있고, 다리의 길이는 다리가 격자에서 차지하는 칸의 수이다. 다리를 연결해서 모든 섬을 연결하려고 한다. 섬 A에서 다리를 통해 섬 B로 갈 수 있을 때, 섬 A와 B를 연결되었다고 한다. 다리의 양 끝은 섬과 인접한 바다 위에 있어야 하고, 한 다리의 방향이 중간에 바뀌면 안된다. 또, 다리의 길이는 2 이상이어야 한다. 다리의 방향이 중간에 바뀌면 안되기 때문에, 다리의 방향은 가로 또는 세로가 될 수 밖에 없다. 방향이 가로인 다리는 다리의 양 끝이 가로 방향으로 섬과 인접해야 하고, 방향이 세로인 다리는 다리의 양 끝이 세로 방향으로 섬과 인접해야 한다. 섬 A와 B를 연결하는 다리가 중간에 섬 C와 인접한 바다를 지나가는 경우에 섬 C는 A, B와 연결되어있는 것이 아니다. 아래 그림은 섬을 모두 연결하는 올바른 2가지 방법이고, 다리는 회색으로 색칠되어 있다. 섬은 정수, 다리는 알파벳 대문자로 구분했다. 다음은 올바르지 않은 3가지 방법이다.
|
입력
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다. |
출력
모든 섬을 연결하는 다리 길이의 최솟값을 출력한다. 모든 섬을 연결하는 것이 불가능하면 -1을 출력한다. |
제한
|
2. 문제풀이
보자마자 모든섬을 연결해야 하는데 최소비용으로 연결해라. 라고 적혀있어서 MST를 떠올렸다. |
3. 소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# Gold 3 17472 다리만들기2
#상 하 좌 우
move_list = [(-1, 0), (1, 0), (0, -1), (0, 1)]
#섬정보를 변경한다.
def change_number(y, x, idx):
for dy, dx in move_list:
d_y, d_x = y+dy, x+dx
if d_y < 0 or d_x < 0 or d_y >= row or d_x >= col:
continue
if island_map[d_y][d_x] == 0:
continue
if island_map[d_y][d_x] == 1:
island_map[d_y][d_x] = idx
change_number(d_y, d_x, idx)
#간선의 정보를 가져온다.
def find_edge(y, x, idx):
for dy, dx in move_list:
d_y, d_x = y+dy, x+dx
count = 0
while True:
#밖으로 벗어나면 취소
if d_y < 0 or d_x < 0 or d_y >= row or d_x >= col: break
#자기자신과 만나면 취소
if island_map[d_y][d_x] == idx: break
#진행방향이 바다라면 한번더 진행하고 거리 count
if island_map[d_y][d_x] == 0:
d_y += dy
d_x += dx
count += 1
continue
#그외의 경우이다.
#길이가 2 이하라면 취소
if count < 2: break
#그게 아니라면 거리 추가하기
num = island_map[d_y][d_x]
cost = distances[idx-2][num-2]
if cost == 0: distances[idx-2][num-2] = count
else: distances[idx-2][num-2] = min(cost, count)
break
#부모의 정보를 가져온다.
def getParent(idx):
if parent[idx] == idx:
return idx
parent[idx] = getParent(parent[idx])
return parent[idx]
#부모를 병합한다. 작은 부모에게 병합
def unionParent(a, b):
a = getParent(a)
b = getParent(b)
if a < b: parent[b] = a
else: parent[a] = b
#부모가 같은지 확인한다.
def find(a, b):
a = getParent(a)
b = getParent(b)
return a == b
#가로세로 크기입력받기
row , col = map(int, input().split())
#부모노드 확인용
parent = [i for i in range(6)]
#각 이동 거리에 대한 배열
distances = [[0 for j in range(6)] for i in range(6)]
#노드 연결 비용
costs = []
#최단거리
answer = 0
#섬정보 저장
island_map = []
for i in range(row):
island_map.append(list(map(int, input().split())))
number = 2
#섬정보 바꾸기
for i in range(row):
for j in range(col):
if island_map[i][j] == 1:
island_map[i][j] = number
change_number(i, j, number)
number += 1
#섬과 연결되있는 부분 찾기
for i in range(row):
for j in range(col):
if island_map[i][j]:
find_edge(i, j, island_map[i][j])
#관리하기 쉽도록 데이터 구조 편집
for i in range(6):
for j in range(6):
if distances[i][j] and [j, i, distances[i][j]] not in costs:
costs.append([i, j, distances[i][j]])
#거리를 기준으로 정렬한다.
costs = sorted(costs, key = lambda x : x[2])
#거리계산하고 부모노드 병합하기
for cost in costs:
if not find(cost[0], cost[1]):
answer += cost[2]
unionParent(cost[0], cost[1])
#모든섬이 연결되었나 확인한다.
for i in range(number-2):
#하나라도 연결이 되어있지 않다면 -1로 바꾼다
if getParent(i) != 0:
answer = -1
break
print(answer)
|
'코딩테스트 > 백준' 카테고리의 다른 글
팰린드롬수 Python(백준, 1259) (1) | 2020.08.18 |
---|---|
단어공부 Python(백준, 1157) (0) | 2020.08.17 |
단어의 개수 Python(백준, 1152) (0) | 2020.08.16 |
직사각형에서 탈출 Python(백준, 1085) (0) | 2020.08.11 |
로봇시뮬레이션 Python(백준, BAEKJOON) (0) | 2020.07.01 |
댓글