문제해결 프로그래밍
최소 비용 신장 트리
강원대학교 컴퓨터공학과 최우혁
강의 순서
2
최소 비용 신장 트리Minimum Spanning Tree
3
트리
최소 비용 신장 트리Minimum Spanning Tree
4
트리
최소 비용 신장 트리Minimum Spanning Tree
5
트리
최소 비용 신장 트리Minimum Spanning Tree
6
최소 비용 신장 트리Minimum Spanning Tree
최소 비용 신장 트리Minimum Spanning Tree
7
최소 비용 신장 트리Minimum Spanning Tree
최소 비용 신장 트리Minimum Spanning Tree
8
문제 정의
최소 비용 신장 트리Minimum Spanning Tree
9
무차별 대입 알고리즘
최소 비용 신장 트리Minimum Spanning Tree
10
무차별 대입 알고리즘
최소 비용 신장 트리Minimum Spanning Tree
11
n | C(n(n - 1) / 2, n - 1) |
1 | 1 |
2 | 1 |
3 | 3 |
4 | 20 |
5 | 210 |
6 | 3,003 |
7 | 54,264 |
8 | 1,184,040 |
9 | 30,260,340 |
10 | 886,163,135 |
프림의 알고리즘: 아이디어
최소 비용 신장 트리Minimum Spanning Tree
12
프림의 알고리즘: 의사 코드Pseudo Code
최소 비용 신장 트리Minimum Spanning Tree
13
프림의 알고리즘: 예
최소 비용 신장 트리Minimum Spanning Tree
14
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
15
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
16
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
17
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
18
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
↓
19
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
20
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
프림의 알고리즘: 구현
최소 비용 신장 트리Minimum Spanning Tree
↓
21
1 import sys
2
3
4 def prim(W, n):
5 F = set()
6 I = [0] * n
7 D = [0] * n
8
9 for i in range(1, n):
10 I[i] = 0
11 D[i] = W[0][i]
12
13 for k in range(n - 1):
14 d_min = sys.maxsize
15 i_min = -1
16
17 for i in range(1, n):
18 if 0 <= D[i] <= d_min:
19 d_min = D[i]
20 i_min = i
21 e = (i_min, I[i_min])
22 F.add(e)
23 D[i_min] = -1
24
25 for i in range(1, n):
26 if W[i_min][i] < D[i]:
27 D[i] = W[i_min][i]
28 I[i] = i_min
29 return F
크루스칼의 알고리즘: 아이디어
최소 비용 신장 트리Minimum Spanning Tree
22
크루스칼의 알고리즘: 의사 코드Pseudo Code
최소 비용 신장 트리Minimum Spanning Tree
23
크루스칼의 알고리즘: 예
최소 비용 신장 트리Minimum Spanning Tree
24
크루스칼의 알고리즘: 최소 가중치 간선 선택
최소 비용 신장 트리Minimum Spanning Tree
25
크루스칼의 알고리즘: 서로소 집합 연산
최소 비용 신장 트리Minimum Spanning Tree
26
크루스칼의 알고리즘: 서로소 집합 연산
최소 비용 신장 트리Minimum Spanning Tree
27
크루스칼의 알고리즘: 서로소 집합 연산
최소 비용 신장 트리Minimum Spanning Tree
28
크루스칼의 알고리즘: 서로소 집합 연산의 예
최소 비용 신장 트리Minimum Spanning Tree
29
크루스칼의 알고리즘: 서로소 집합 연산의 구현
최소 비용 신장 트리Minimum Spanning Tree
30
1 def make_set(D, R, i):
2 D[i] = i
3 R[i] = 0
4
5
6 def find(D, i):
7 while D[i] != i:
8 i = D[i]
9 return i
10
11
12 def union(D, R, u, v):
13 if u == v:
14 return
15 if R[u] > R[v]:
16 D[v] = u
17 elif R[u] < R[v]:
18 D[u] = v
19 else:
20 D[v] = u
21 R[u] = R[u] + 1
크루스칼의 알고리즘: 최종 구현
최소 비용 신장 트리Minimum Spanning Tree
31
1 import sys
2 import heapq
3
4 def kruskal(W, n):
5 F, D, R, E = set(), [0] * n, [0] * n, dict()
6
7 for i in range(n):
8 for j in range(n):
9 e1, e2, w = (i, j), (j, i), W[i][j]
10 if e1 not in E and e2 not in E and w != sys.maxsize and i != j:
11 E[e1] = w
12
13 E = [(v, k) for k, v in E.items()]
14 heapq.heapify(E)
15 m = len(E)
16
17 for i in range(n):
18 make_set(D, R, i)
19
20 for _ in range(m):
21 _, (i, j) = heapq.heappop(E)
22 u, v = find(D, i), find(D, j)
23 if u != v:
24 union(D, R, u, v)
25 F.add((i + 1, j + 1))
26 if len(F) == n - 1:
27 break
28 return F
프림 알고리즘과 크루스칼 알고리즘의 성능 비교
최소 비용 신장 트리Minimum Spanning Tree
32
알고리즘 | 시간복잡도 | 간선이 적은 경우 Sparse Graph (m ≓ n) | 간선이 많은 경우 Dense Graph (m ≓ n2) |
프림의 알고리즘 | θ(n2) | θ(n2) | θ(n2) |
크루스칼의 알고리즘 | θ(m log m) + θ(m log n) | θ(n log n) | θ(n2 log n) |
프림의 알고리즘의 개선 방법
최소 비용 신장 트리Minimum Spanning Tree
33
자료구조 | 시간복잡도 | 간선이 적은 경우 Sparse Graph (m ≓ n) | 간선이 많은 경우 Dense Graph (m ≓ n2) |
배열 | θ(n2) | θ(n2) | θ(n2) |
힙 Heap | θ(m log n) | θ(n log n) | θ(n2 log n) |
크루스칼의 알고리즘의 개선 방법
최소 비용 신장 트리Minimum Spanning Tree
34
1 def find_path_compression(D, i):
2 if D[i] != i:
3 D[i] = find_path_compression(D, D[i])
4 return D[i]
문제: 주근깨Freckles
35
설명
문제: 주근깨Freckles
36
설명
문제: 주근깨Freckles
37
예시
문제: 주근깨Freckles
입력
출력
38
1
3
1.0 1.0
2.0 2.0
2.0 4.0
3.41
테스트 케이스 개수
주근깨의 개수
잉크의 소모량 (소수점 둘째자리)
주근깨의 x, y 좌표
힌트
문제: 주근깨Freckles
39
문제: 여행자 가이드Tourist Guide
40
설명
문제: 여행자 가이드Tourist Guide
41
예시
문제: 여행자 가이드Tourist Guide
입력
출력
42
6
sugarloaf
maracana
copacabana
ipanema
corcovado
lapa
7
ipanema copacabana
copacabana sugarloaf
ipanema sugarloaf
maracana lapa
sugarloaf maracana
corcovado sugarloaf
lapa corcovado
5
guanabarabay
…
City map #1: 1 camera(s) found
sugarloaf
City map #2: 2 camera(s) found
sambodromo
guanabarabay
지역의 개수 N
지역의 이름
도로의 개수
도로의 연결 정보
다음 테스트케이스
첫번째 테스트케이스의 카메라 대수
카메라가 설치된 지역
여러 지역에 카메라가 설치 시
매 줄마다 지역을 출력
힌트
문제: 여행자 가이드Tourist Guide
43
문제: 티모의 벼농사Teemo’s Rice Farming
44
설명
문제: 티모의 벼농사Teemo’s Rice Farming
45
설명
문제: 티모의 벼농사Teemo’s Rice Farming
46
예시
문제: 티모의 벼농사Teemo’s Rice Farming
입력
출력
47
10
537
914
269
536
882
245
172
615
367
688
0 56 91 16 53 82 30 52 51 51
56 0 36 24 12 41 67 50 37 99
91 36 0 96 6 34 2 60 74 58
16 24 96 0 25 27 8 79 34 14
53 12 6 25 0 12 23 30 44 7
82 41 34 27 12 0 79 68 87 66
30 67 2 8 23 79 0 39 32 14
52 50 60 79 30 68 39 0 57 38
51 37 74 34 44 87 32 57 0 1
51 99 58 14 7 66 14 38 1 0
266
논의 개수 N
자동 급수 장치를 설치하는 비용 Wi
모든 논에 물을 공급하는 최소 비용
i번째 논과 j번째 논 사이에 파이프를 설치하는 비용 Pi,j
힌트
문제: 티모의 벼농사Teemo’s Rice Farming
48