본문 바로가기
카테고리 없음

백준 17144번: 미세먼지 안녕!

by redcubes 2025. 12. 25.

문제 링크

📋 문제 요약

R×C 크기의 방에 미세먼지와 공기청정기가 있다. T초 동안 매 초마다 다음 두 가지 일이 순서대로 일어난다:

  1. 미세먼지 확산: 모든 칸에서 동시에 인접 4방향으로 확산
  2. 공기청정기 작동: 바람이 불어 미세먼지가 순환

T초 후 남아있는 미세먼지의 총량을 구하는 문제다.


🔍 문제 분석

1. 미세먼지 확산 규칙

각 칸 (r, c)에 있는 미세먼지 A가 확산될 때:

  • 인접한 4방향(상하좌우)으로 각각 ⌊A/5⌋ 만큼 확산
  • 공기청정기가 있거나 격자 밖이면 그 방향으로는 확산 안 됨
  • 원래 칸에 남는 양: A - ⌊A/5⌋ × (확산된 방향 수)

핵심 포인트: 모든 칸에서 동시에 확산이 일어난다. 따라서 확산 결과를 새로운 배열에 저장해야 한다.

2. 공기청정기 바람 순환

공기청정기는 1열에 두 행을 차지하며 설치되어 있다:

  • 위쪽 공기청정기: 반시계방향 순환
  • 아래쪽 공기청정기: 시계방향 순환

바람이 불면 미세먼지가 한 칸씩 밀려나고, 공기청정기에서 나오는 바람은 깨끗하며(0), 공기청정기로 들어간 미세먼지는 정화된다(사라짐).


💡 솔루션 도출

Step 1: 전체 구조 설계

전형적인 시뮬레이션 문제다. T번의 루프를 돌면서 매번 확산과 순환을 수행하면 된다.

for _ in range(T):
    확산()
    순환()

Step 2: 확산 구현 전략

"동시에 확산"을 구현하는 가장 확실한 방법은 새 배열을 만들어서 결과를 저장하는 것이다.

# 의사코드
new_grid = 빈 배열 생성
for 모든 칸 (r, c):
    if 공기청정기:
        new_grid[r][c] = -1
    elif 미세먼지 있음:
        확산량 = grid[r][c] // 5
        for 인접 4방향:
            if 유효한 칸 and 공기청정기 아님:
                new_grid[인접칸] += 확산량
                확산 횟수 += 1
        new_grid[r][c] += grid[r][c] - 확산량 * 확산 횟수
grid = new_grid

Step 3: 순환 구현 전략

순환은 "밀기"보다 "당기기"로 구현하는 것이 덜 헷갈린다.

예를 들어 위쪽 공기청정기(반시계방향)의 경우:

공기청정기 위치를 top이라 하면:

1. 왼쪽 열: 위로 당기기 (아래→위)
   g[r][0] = g[r-1][0]  (r: top-1 → 1)

2. 윗줄: 왼쪽으로 당기기 (오른쪽→왼쪽)
   g[0][c] = g[0][c+1]  (c: 0 → C-2)

3. 오른쪽 열: 아래로 당기기 (위→아래)
   g[r][C-1] = g[r+1][C-1]  (r: 0 → top-1)

4. 공기청정기 행: 오른쪽으로 당기기 (왼쪽→오른쪽)
   g[top][c] = g[top][c-1]  (c: C-1 → 2)

5. 공기청정기에서 나오는 바람은 깨끗
   g[top][1] = 0

순서가 중요하다! 값이 덮어씌워지기 전에 당겨와야 하므로, 공기청정기에서 멀어지는 방향부터 처리한다.

Step 4: 공기청정기 위치 찾기

입력에서 -1이 나오는 두 행을 찾으면 된다. 문제 조건상 두 -1은 연속된 행에 있다.


⚡ 최적화 포인트

1. 입력 최적화

한 번에 모든 입력을 읽어서 처리:

R,C,T,*d = map(int, open(0).read().split())
g = [[d[r*C+c] for c in range(C)] for r in range(R)]

2. 조건 분기 최소화

확산 시 v=0인 경우를 elif v:로 빠르게 스킵:

if v == -1:
    n[r][c] = -1
elif v:  # v > 0인 경우만 처리
    # 확산 로직

3. 함수 호출 오버헤드 제거

함수로 분리하지 않고 메인 루프에 인라인으로 작성하면 약간의 성능 향상이 있다.

4. 결과 계산

전체 합계에서 공기청정기(-1 두 개)를 보정:

print(sum(sum(row) for row in g) + 2)

✅ 최종 코드

def s():
    R,C,T,*d=map(int,open(0).read().split())
    g=[[d[r*C+c]for c in range(C)]for r in range(R)]
    t=b=0
    for r in range(R):
        if g[r][0]==-1:
            if t:
                b=r
            else:
                t=r
    for _ in range(T):
        n=[[0]*C for _ in range(R)]
        for r in range(R):
            for c in range(C):
                v=g[r][c]
                if v==-1:
                    n[r][c]=-1
                elif v:
                    a=v//5
                    k=0
                    for nr,nc in((r-1,c),(r+1,c),(r,c-1),(r,c+1)):
                        if 0<=nr

🔑 변수 설명

변수 의미
g 현재 격자 상태
n 확산 후 새 격자
t 위쪽 공기청정기 행 (top)
b 아래쪽 공기청정기 행 (bottom)
v 현재 칸의 미세먼지 양 (value)
a 확산량 v//5 (amount)
k 확산된 방향 수 (count)

⏱️ 시간복잡도

O(T × R × C)

  • T: 최대 1,000
  • R, C: 최대 50
  • 총 연산: 약 250만 회 → 1초 내 충분

📝 실수하기 쉬운 포인트

  1. 확산의 동시성: 새 배열을 사용하지 않으면 이미 확산된 값이 다시 확산되는 오류 발생
  2. 순환 순서: 당기는 순서가 잘못되면 값이 덮어씌워져서 손실됨
  3. 공기청정기 출력: 순환 후 g[t][1] = 0g[b][1] = 0을 빼먹으면 안 됨
  4. 결과 보정: 공기청정기 -1 두 개를 더해줘야 정확한 미세먼지 총량이 됨