본문 바로가기

프로그래머가 되자!!!!/알고리즘 공부!

[BOJ] 16234. 인구이동 (python)

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

 

16234번: 인구 이동

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모

www.acmicpc.net

 

문제

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모든 나라는 1×1 크기이기 때문에, 모든 국경선은 정사각형 형태이다.

오늘부터 인구 이동이 시작되는 날이다.

인구 이동은 하루 동안 다음과 같이 진행되고, 더 이상 아래 방법에 의해 인구 이동이 없을 때까지 지속된다.

  • 국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면, 두 나라가 공유하는 국경선을 오늘 하루 동안 연다.
  • 위의 조건에 의해 열어야하는 국경선이 모두 열렸다면, 인구 이동을 시작한다.
  • 국경선이 열려있어 인접한 칸만을 이용해 이동할 수 있으면, 그 나라를 오늘 하루 동안은 연합이라고 한다.
  • 연합을 이루고 있는 각 칸의 인구수는 (연합의 인구수) / (연합을 이루고 있는 칸의 개수)가 된다. 편의상 소수점은 버린다.
  • 연합을 해체하고, 모든 국경선을 닫는다.

각 나라의 인구수가 주어졌을 때, 인구 이동이 며칠 동안 발생하는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N, L, R이 주어진다. (1 ≤ N ≤ 50, 1 ≤ L ≤ R ≤ 100)

둘째 줄부터 N개의 줄에 각 나라의 인구수가 주어진다. r행 c열에 주어지는 정수는 A[r][c]의 값이다. (0 ≤ A[r][c] ≤ 100)

인구 이동이 발생하는 일수가 2,000번 보다 작거나 같은 입력만 주어진다.

 

 

풀이

조금 무지성(?)으로 풀어버렸다... 중간에 통화도 하고 빨래도 개고 하는 accident때문이라고 하자!

우선 문제는 로직이 어렵지는 않다. 풀이법은 명확하나, 구현문제라서 조금 귀찮고 더 효율적인 방법을 고민하게 된다.

1. union을 파악한다. (즉 연결된 나라를 파악한다) -> bfs를 이용 + bfs로 탐색하며 탐색 완료한 애들을 list에 담아두어, 한번에 같은 인구수로 변환시켜준다.
2. 새로운 union을 찾아 반복하고, 전체가 완료되면 1일이 지난다.
3. 인구수 변경을 진행했다면 추가 1일, 아니라면 종료하여 총 걸린 일수를 출력한다.

 

 

코드

 

import sys
read = sys.stdin.readline
import collections



size_N, L, R = map(int, read().strip().split())
population_map = [list(map(int, read().strip().split())) for _ in range(size_N)]

dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]

def check_least_one_open(x, y):

    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]

        if 0 <= nx < size_N and 0 <= ny < size_N and L <= abs(population_map[x][y] - population_map[nx][ny]) <= R:
            if checker_is_open[nx][ny] == 0:
                checker_is_open[x][y] = 1
                return True



def make_union_population(x, y):
    temp_union = [(x, y)]
    count_union = 1
    count_population = population_map[x][y]
    queue = collections.deque()
    queue.appendleft((x, y))

    while queue:
        p, q = queue.popleft()
        for i in range(4):
            nx = p + dx[i]
            ny = q + dy[i]
            if 0 <= nx < size_N and 0 <= ny < size_N and L <= abs(population_map[p][q] - population_map[nx][ny]) <= R:
                if checker_is_open[nx][ny] == 0:
                    checker_is_open[nx][ny] = 1
                    queue.appendleft((nx, ny))
                    count_union += 1
                    count_population += population_map[nx][ny]
                    temp_union.append((nx, ny))
    union_population = count_population // count_union
    for union in temp_union:
        population_map[union[0]][union[1]] = union_population

def is_not_finish(graph):
    for i in range(size_N):
        for j in range(size_N):
            if graph[i][j] == 1:
                return True
    return False



move_day = 0
checker = [[0] * size_N for _ in range(size_N)]
is_not_finished = True


while is_not_finished:
    checker_is_open = [[0] * size_N for _ in range(size_N)]
    for r in range(size_N):
        for c in range(size_N):
            if checker_is_open[r][c] == 0 and check_least_one_open(r, c):
                make_union_population(r, c)
                is_not_finished = False
    if is_not_finished == True:
        break
    move_day += 1
    is_not_finished = is_not_finish(checker_is_open)
print(move_day)