Home 인구 이동 백준 16234번 BFS
Post
Cancel

인구 이동 백준 16234번 BFS

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

문제

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

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

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

국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면, 두 나라가 공유하는 국경선을 오늘 하루 동안 연다.

위의 조건에 의해 열어야하는 국경선이 모두 열렸다면, 인구 이동을 시작한다.

국경선이 열려있어 인접한 칸만을 이용해 이동할 수 있으면, 그 나라를 오늘 하루 동안은 연합이라고 한다.

연합을 이루고 있는 각 칸의 인구수는 (연합의 인구수) / (연합을 이루고 있는 칸의 개수)가 된다. 편의상 소수점은 버린다.연합을 해체하고, 모든 국경선을 닫는다.

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

보통 이렇게 2x2 형태의 그리드로 자료가 주어지고 각 셀단위로 주변에 영향을주고 움직이고 하면 거의 DFS/BFS의 전형적인 문제이다. 모든 나라의 위치에서 상,하,좌우로 국경선을 열 수 있는지 확인을 해야 하고 모든 나라의 위치에서 DFS, BFS를 수행하면 인접한 나라의 인구수를 확인한 뒤에, 이동이 가능하다면 국경선을 열고 인구 이동 처리를 진행하면 된다.

  • 정답은 그럭저럭 나오는데 백준에서는 시간초과 뜸 ```py from collections import deque

#땅의 크기(N), L,R값을 입력받기 n,l,r = map(int, input().split())

#전체 나라의 정보(NxN)를 입력받기 graph = [] for _ in range(n): graph.append(list(map(int,input().split())))

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

result = 0

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
일단 deque와 각 정보를 먼저 받아주는 것으로 시작을 합시다. 

```py
#특정 위치에서 출발하여 모든 연합을 체크한 뒤에 데이터 갱신
def process(x,y,index):
    #(x,y)의 위치와 연결된 나라(연합) 정보를 담는리스트
    united = []
    united.append((x,y)) #현재 연합의 번호 할당
    
    #너비 우선 탐색(BFS)을 위한 큐 자료구조 정의
    q = deque()
    q.append((x,y))
    union[x][y] = index #현재 연합의 번호 할당
    summary = graph[x][y] #현재 연합의 전체 인구 수
    count = 1 #현재 연합의 국가 수
    
    #큐가 빌 때까지 반복(BFS)
    while q:
        x,y = q.popleft()
        #현재 위치에서 4가지 방향을 확인하며
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            #바로 옆에 있는 나라를 확인하여
            if 0 <= nx < n and  0 <= ny < n and union[nx][ny] == -1:
                #옆에 있는 나라와 인구 차이가 L명 이상, R명 이하라면
                if l <= abs(graph[nx][ny] - graph[x][y]) <= r:
                    q.append((nx,ny))
                    #연합에 추가
                    union[nx][ny] = index
                    summary += graph[nx][ny]
                    count += 1
                    united.append((nx,ny))
                    
    #연합 국가끼리 인구를 분배
    for i, j in united:
        graph[i][j] = summary // count
    return count

연합을 체크하고 데이터를 갱신하는 부분입니다. 차례대로 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
#특정 위치에서 출발하여 모든 연합을 체크한 뒤에 데이터 갱신
def process(x,y,index):
    #(x,y)의 위치와 연결된 나라(연합) 정보를 담는리스트
    united = []
    united.append((x,y)) #현재 연합의 번호 할당
    
    #너비 우선 탐색(BFS)을 위한 큐 자료구조 정의
    q = deque()
    q.append((x,y))
    union[x][y] = index #현재 연합의 번호 할당
    summary = graph[x][y] #현재 연합의 전체 인구 수
    count = 1 #현재 연합의 국가 수

process는 x와 y의 좌표와 index의 값을 받습니다. index는 현재 몇번이 진행되고 있는지 연합을 확인하기 위해서 사용합니다.

q = deque() 에서 union이 들어갈 나라를 관리하기 위해서 사용합니다.

일단 첫번째로 들어온 위치를 하나의 연합으로 가정하는 것으로 시작을 합니다.

그리고 summary도 현재위치의 인구와 count도 아직 1국가이니 1로 초기화를 하는 것으로 시작을 합니다. 이제 순회를 돌면서 확인을 해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#큐가 빌 때까지 반복(BFS)
    while q:
        x,y = q.popleft()
        #현재 위치에서 4가지 방향을 확인하며
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            #바로 옆에 있는 나라를 확인하여
            if 0 <= nx < n and  0 <= ny < n and union[nx][ny] == -1:
                #옆에 있는 나라와 인구 차이가 L명 이상, R명 이하라면
                if l <= abs(graph[nx][ny] - graph[x][y]) <= r:
                    q.append((nx,ny))
                    #연합에 추가
                    union[nx][ny] = index
                    summary += graph[nx][ny]
                    count += 1
                    united.append((nx,ny))

q 연합을 저장하는 큐가 빌때까지 반복을 하고 append는 오른쪽 기준이지만 popleft는 왼쪽에서 뺍니다.

즉 먼저 들어온 순서대로 큐가 관리됩니다.

이제 for문으로 4망향을 살펴보면서 확인을 해봅시다.

그렇게 간 나라가 범위를 벗어나지 않았고 -1이라면(나중에 union을 -1로 초기화 하는 부분이 나옵니다. 확인을 해봅시다.

옆에 있는 나라와 인구수의 차이가 l이상 r이하가 된다면 q에다가 추가를 해서 연합으로 관리를 해줍시다.

그리고 동시에 summary에 추가를 하고 count역시 증가를 시키게 됩니다.

1
2
3
4
#연합 국가끼리 인구를 분배
    for i, j in united:
        graph[i][j] = summary // count
    return count

이가 끝난다면 연합 국가끼리 인구를 분배합시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
total_count = 0

#더 이상 인구 이동을 할 수 없을 때까지 반복
while True:
    union = [[-1]*n for _ in range(n)]
    index = 0
    for i in range(n):
        for j in range(n):
            if union[i][j] == -1: #해당 나라가 아직 처리되지 않았다면
                process(i,j,index)
                index += 1
        #모든 인구 이동이 끝난 경우
    if index == n*n:
        break
    total_count += 1

#인구 이동 횟수 출력
print(total_count)

마지막 결과물인 total_count = 0을 두고

while을 무한 반복으로 돌리는걸로 시작을 합시다. union을 일단 -1로 초기화를 해두고

이중 for문을 이용해서 union을 확인을 합니다. 해당 나라가 아직 처리되지 않았다면 process함수를 이용하여 확인을 합니다.

이렇게 인구 이동이 한번 끝날때마다 total_count의 카운트를 증가시킨후 반환합니다.

내가 관리를 해야할 함수와 어떤 것을 필요한지 곰곰히 생각해봐야 겠습니다.

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.