Yeon's 개발블로그

지식을 전파하는 개발자가 되고싶습니다.

Algorithms/Samsung

[python] BOJ 백준 16234: 인구이동

Dev.yeon 2022. 4. 25. 16:40

1. 문제  

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

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

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

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

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

2. 입력   

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

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

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

3. 출력   

인구 이동이 며칠 동안 발생하는지 첫째 줄에 출력한다.

4. 해결방법   

이 문제는 구현, BFS 문제이다. 상하좌우를 탐색하면서 국경선을 열 연합들을 탐색해야하므로 큐를 이용한 BFS탐색을 해야한다. 다만 하루에 인구이동이 여러개의 연합에서 이루어질 수도 있으므로, 방문여부를 확인하는 배열을 메인함수에 두어서 연합을 탐색해 주어야한다. 방문하지 않은 탐색 좌표에 대해서 새로운 연합을 생성할수 있기 때문이다. 국경이 더이상 안열리는 때를 체크를 해주어야하는데, flag변수 하나만있으면 가능하다. flag변수의 초기값을 false로 두고, or 계산을 실행하면서 한번이라도 국경이 열렸으면 True로 바뀌게끔 계산해주면된다. 

1) bfs 함수: 이 함수에서는 연합을 탐색하고, 생긴 연합에 대해서 인구이동을 같이 해주는 것을 모두 구현해주었다. 인구수 총합을 누적하는 변수 people과 연합나라들 좌표를 저장해두는 union 배열을 생성해주어 관리하였다. 현재좌표를 큐에 저장해주고, 큐를 돌면서 좌표들의 상하좌우값에 대해서 국경선을 열수 있는지 판단해주고, 만약국경선이 열리는 나라라면 그것도 큐에 넣어주어 계속해서 연합을 탐색해주면된다. 이때 국경선이 안열렸다면 union 배열에는 입력으로 들어온 좌표 1개만 있을 것이고, 그렇지 않은 경우에는 인구이동을 시켜주어 board값을 업데이트 해주면된다. 

 

5) 구현코드   

from collections import deque
n,l,r=map(int,input().split())
board=[list(map(int,input().split())) for _ in range(n)]

#상하좌우 
dx=[0,0,1,-1]
dy=[1,-1,0,0]

#연합 탐색 후 인구이동 
def bfs(x,y, visited,board):
  q=deque()
  q.append([x,y]) #큐 저장 
  people=board[x][y] #인구수 총합 
  union=[[x,y]] #연합나라들 좌표 
  visited.add((x,y)) #방문여부 체크 

  while q:
    x,y=q.popleft()
    for i in range(4): #상하좌우 탐색 
      nx,ny=x+dx[i],y+dy[i]
      #범위안에 있고 다른연합이 아니라면 
      if 0<=nx<n and 0<=ny<n and (nx,ny) not in visited:
        #인구차이 조건 만족하면 
        if l<=abs(board[nx][ny]-board[x][y])<=r:
          q.append([nx,ny]) #큐에 저장 
          people+=board[nx][ny] #인구수 총합 업데이트 
          union.append([nx,ny]) #연합나라 업데이트 
          visited.add((nx,ny)) #방문 
  #연합을 이루고 있는 인구수 조정 
  people=people//len(union)

  if len(union)==1: #국경선이 안열린 경우 
    return board,visited,False 

  for x,y in union:
    board[x][y]=people #인구이동 
  return board,visited,True #국경선이 열린 경우 

flag=True 
day=0
while flag:
  flag=False #국경선이 모두 닫힌경우 체크 
  visited=set() #방문여부체크 
  for i in range(n):
    for j in range(n):
      #방문하지 않은 좌표들만 탐색 
      if (i,j) not in visited:
        board,visited,change=bfs(i,j,visited,board)
        flag=flag or change #한번도 국경선이 안열렸으면 종료 
  day+=1 #인구이동날짜 증가 

print(day-1)

 

⭐️난이도:  골드 5⭐️

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

 

16234번: 인구 이동

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

www.acmicpc.net