사용한 알고리즘 / 자료구조
- 델타를 이용한 이차원 배열 탐색.
- 유니온 파인드
- 해시 테이블(딕셔너리, 셋)
접근한 방법
- 지도에 각 문명의 정보를 표시한다.
- set에 '통합되지 않은 문명 번호'를 기입한다.
- dict에 '각 문명별 발상지' 를 기입한다. key는 문명 번호, value는 그 문명 발상지의 좌표.
정보를 입력받은 뒤에 이차원 배열을 탐색한다. 다른 문명과 인접할 때마다 Union연산을 수행한다. 문명이 모두 통합되었다면 탐색을 종료한다.
+ 각 문명이 통합될 때, dict에서 '문명별 발상지' 의 좌표 또한 업데이트한다. 이렇게 하면 타일에 '아직 통합되지 않은 문명 번호'가 남아있다 하더라도 그 숫자들을 '통합된 문명 번호' 취급할 수 있다.
개선 가능한 점
- 4 방향 탐색 부분 코드가 계속 반복된다. 해당 부분을 함수화했다면 훨씬 코드가 간결해졌을 것이다.
- origin_set의 길이를 참조하는 대신, 문명의 통합 여부를 flag list와 정수(ex: independent_civs)로 남은 문명의 갯수를 세었다면 훨씬 실행시간을 줄일 수 있었을 것.
- 코드의 번잡함. 유사한 실행시간에 코드 길이가 절반정도 되는 풀이를 봤다.
어려웠던 점
- 구현해야 할 양이 많은 편이었다.
'상어 초등학교'를 풀이할 때에도 비슷한 풀이를 활용했다.
https://www.acmicpc.net/problem/21608
21608번: 상어 초등학교
상어 초등학교에는 교실이 하나 있고, 교실은 N×N 크기의 격자로 나타낼 수 있다. 학교에 다니는 학생의 수는 N2명이다. 오늘은 모든 학생의 자리를 정하는 날이다. 학생은 1번부터 N2번까지 번호
www.acmicpc.net
https://programmer-565.tistory.com/22
21608. 상어 초등학교 - 구현, 시뮬레이션, counter sort, 해시 테이블. 파이썬
사용한 알고리즘/자료구조 델타를 이용한 2차원 배열 탐색 counter sort 해시 테이블(딕셔너리) 접근한 방법 학생의 위치를 정할 때마다 모든 좌표를 순회하며 주위 4 칸의 좌표 정보를 확인하는 브
programmer-565.tistory.com
느낀 점
- 문제를 꼼꼼하게 읽고 전체 문제를 작은 단위로 분할한다.
풀이했을 당시의 백준 티어
2021-10-18, 골드 3
실행시간, 메모리, 언어
2388ms, 263556kb, Pypy3 - 100점
2021-11-03 현재 실행시간 1등
6888ms, 95924kb, Python3- 54점
from sys import stdin
from collections import deque
# 1. 유니온 함수.
def union(a_row, a_col, b_row, b_col):
# 1) 예외 처리
origin_a = find(a_row, a_col)
origin_b = find(b_row, b_col)
# 만약 a 지점과 b 지점의 조상이 같다면
if origin_b == origin_a:
return # 연산하지 않음.
# 2) 연산
# (1) b가 통합된 문명일 때
if origin_b not in origin_set:
# b의 좌표에 a의 조상 문명 번호를 지도에 기입.
MAP[b_row][b_col] = origin_a
return
# (2) a가 통합된 문명일 때
if origin_a not in origin_set:
# a의 좌표에 b의 조상 문명 번호를 지도에 기입.
MAP[a_row][a_col] = origin_b
return
# (3) a와 b 모두가 존재할 때
# 문명 번호가 더 작은 것을 우선순위로 통합.
if origin_a < origin_b:
MAP[b_row][b_col] = origin_a
origin_set.remove(origin_b)
origin_location[origin_b] = origin_location[origin_a]
return
else:
MAP[a_row][a_col] = origin_b
origin_set.remove(origin_a)
origin_location[origin_a] = origin_location[origin_b]
return
# 2. find 함수
def find(row, col):
# 1) 선언
# 현재 조상을 확인하고 싶은 값.
x = MAP[row][col]
# 2) 연산
# (1) 예외처리: 현재 좌표값이 '근원지' 중 하나일 때
if x in origin_set:
return x # 그대로 리턴
# (2) 부모 정보 업데이트
# a. origin_location 딕셔너리에서 x값의 '근원지' 값을 가져옴
x_row, x_col = origin_location[x]
# b. find
origin = find(x_row, x_col)
# c. 조상 정보 갱신
MAP[row][col] = origin # 조상 정보를 갱신.
# 3) 반환
return origin
# 델타를 이용한 2차원 배열 탐색.
DX = (0, 0, -1, 1) # 상, 하, 좌, 우
DY = (-1, 1, 0, 0) # 상, 하, 좌, 우
# 1. 입력
N, K = map(int, stdin.readline().split())
# 1-1. 추가 입력을 위한 선언
# 1) 각 좌표별 문명 정보를 기록하는 2차원 배열
MAP = [[0] * N for _ in range(N)]
# 2) key: 문명 번호, value: 문명 번호의 발상지
origin_location = dict()
# 3) 아직 통합되지 않은 발상지 번호들의 집합.
origin_set = set(range(1, K + 1))
# 2. 선언
civ_que = deque() # 문명들 정보를 저장하는 que
next_que = deque() # BFS 연산을 위한 que (2)
# 1-2. 문명 발상지 정보 입력.
# 문명 번호 1번부터 K 번 까지
for civilization in range(1, K + 1):
# 1) 입력
# 행, 열 좌표를 입력받아
row, col = map(int, stdin.readline().split())
row -= 1
col -= 1
# 2) 연산
# (1) 지도에 배치
MAP[row][col] = civilization
# (2) 딕셔너리에 기입
origin_location[civilization] = (row, col)
# (3) 큐에 기입
civ_que.append((row, col))
# 1-3. 문명 발상지 상쇄 .
for civ in civ_que:
row, col = civ
# 4방향을 순회하며
for direction in range(4):
next_row = row + DY[direction]
next_col = col + DX[direction]
# 인접 문명이 있을 경우
if N > next_row >= 0 and N > next_col >= 0\
and MAP[next_row][next_col]:
# 문명 통합.
union(row, col, next_row, next_col)
# 2. 연산
result = 0
# 종료조건:
# 문명들이 모두 1개로 통합되었을 때
while len(origin_set) != 1:
result += 1
# BFS 연산
while civ_que:
# 큐에 있는 모든 문명 좌표를 순회.
row, col = civ_que.popleft()
# 현재 좌표에 인접한 4 방향을 순회하며
for direction in range(4):
next_row = row + DY[direction]
next_col = col + DX[direction]
# 인덱스 값이 정상이고
if N > next_row >= 0 and N > next_col >= 0:
# 1) 문명이 전파되지 않은 지역이라면
if not MAP[next_row][next_col]:
# 문명을 전파
MAP[next_row][next_col] = MAP[row][col]
# 다음 큐에 입력
next_que.append((next_row, next_col))
# (1) 문명 전파 후 옆에 다른 문명이 있는지 다시 확인.
for next_direction in range(4):
nnext_row = next_row + DY[next_direction]
nnext_col = next_col + DX[next_direction]
# 4 방향 순회 후
if N > nnext_row >= 0 and N > nnext_col >= 0\
and MAP[nnext_row][nnext_col]\
and MAP[nnext_row][nnext_col] != MAP[next_row][next_col]:
# 인접한 문명을 다시 통합.
union(next_row, next_col, nnext_row, nnext_col)
# 2) 문명이 전파되었으며 아직 통합되지 않았다면
elif MAP[next_row][next_col] != MAP[row][col]:
union(row, col, next_row, next_col)
# 현재 큐-다음 큐 교체 연산.
next_que, civ_que = civ_que, next_que
# 4 .출력.
print(result)