본문 바로가기

개발/algorithm

[백준 23289번] 온풍기 안녕! - Python

문제 링크

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

 

23289번: 온풍기 안녕!

유난히 추운 날씨가 예상되는 이번 겨울을 대비하기 위해 구사과는 온풍기를 설치하려고 한다. 온풍기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기

www.acmicpc.net

 

풀이

다른 풀이를 참고했는데도, 어려웠던 문제 

 

벽을 체크하기 위해서 두가지 배열을 사용하였다. 

wall_col[x][y] 는 해당 위치의 오른쪽에 세로 벽이 세워져 있는지 체크

wall_row[x][y]는 해당 위치의 위쪽에 가로 벽이 세워져 있는지 체크 

현재 위치에서 위 아래 왼쪽 오른쪽에 벽이 세워져 있는지 체크하는 함수를 각각 만들었다. 

 

오른쪽과 위쪽은 위의 배열을 통해 간단히 확인할 수 있고, 

왼쪽에 벽에 있는지 확인하려면 왼쪽으로 한칸 이동 후 그 위치에서 wall_col, 즉 오른쪽에 벽이 세워져 있는지 확인하면 된다. 

아래쪽에 벽에 있는지 확인하려면 아래쪽으로 한칸 이동 후 그 위치에서 wall_row, 즉 위쪽에 벽이 세워져 있는지 확인하면 된다. 

 

1. search에는 온도를 확인할 위치를, machine에는 온풍기의 위치를 기록한다.

2. 입력된 조건에 맞게 wall_col과 wall_row를 표시한다. 

 

먹은 초콜렛의 개수가 100개가 넘거나, search에 저장된 확인할 위치의 온도들이 모두 k도 이상이면 아래 과정을 중단한다.

1. machine 에 저장된 위치마다 온풍기 바람을 확산시킨다. : below 함수 

def blow():
    for d, x, y in machine:
        spread(d, x, y)

2. 온풍기 바람 확산 : spread 함수 

예를 들어 오른쪽으로 확산하는 경우, 오른쪽으로 한칸 이동하고 5를 표시한다. 

다음 (x+1, y+1) , (x, y+1), (x-1, y+1) 위치에 확산시켜야 한다. 

1) (x+1, y+1) 로 확산하는 경우 

현재 위치에서 위쪽에 벽이 없고 위쪽 칸의 오른쪽에 벽이 없어야 한다. 

2) (x, y+1) 로 확산하는 경우 

현재 위치에서 오른쪽에 벽이 없어야 한다.

3) (x-1, y+1) 로 확산하는 경우

현재 위치에서 아래쪽에 벽이 없고 아래쪽 칸의 오른쪽에 벽이 없어야 한다. 

 

해당 과정을 각 방향에 맞춰 실행시켜주면 된다. 

def spread(d, x, y):
    tmp = [[0] * c for _ in range(r)]
    q = deque()
	
    # 이동한 위치
    nx = x + dx[d]
    ny = y + dy[d]

	# 5 표시 
    tmp[nx][ny] += 5
    # 위치와 퍼질 바람 양 
    q.append((nx, ny, 5))

    while q:
        x, y, cnt = q.popleft()
			
        if cnt < 1:
            break

        # 오른쪽 확산인 경우 
        if d == 0:
            if up(x, y, 2):
                if right(x - 1, y, 0):
                    q.append((x - 1, y + 1, cnt - 1))
                    tmp[x - 1][y + 1] = cnt - 1
            if right(x, y, 0):
                q.append((x, y + 1, cnt - 1))
                tmp[x][y + 1] = cnt - 1
            if down(x, y, 3):
                if right(x + 1, y, 0):
                    q.append((x + 1, y + 1, cnt - 1))
                    tmp[x + 1][y + 1] = cnt - 1
        # 왼쪽 확산인 경우 
        if d == 1:
            if up(x, y, 2):
                if left(x - 1, y, 1):
                    q.append((x - 1, y - 1, cnt - 1))
                    tmp[x - 1][y - 1] = cnt - 1
            if left(x, y, 1):
                q.append((x, y - 1, cnt - 1))
                tmp[x][y - 1] = cnt - 1
            if down(x, y, 3):
                if left(x + 1, y, 1):
                    q.append((x + 1, y - 1, cnt - 1))
                    tmp[x + 1][y - 1] = cnt - 1
        # 위쪽 확산인 경우 
        if d == 2:
            if right(x, y, 0):
                if up(x, y + 1, 2):
                    q.append((x - 1, y + 1, cnt - 1))
                    tmp[x - 1][y + 1] = cnt - 1
            if up(x, y, 2):
                q.append((x - 1, y, cnt - 1))
                tmp[x - 1][y] = cnt - 1
            if left(x, y, 1):
                if up(x, y - 1, 2):
                    q.append((x - 1, y - 1, cnt - 1))
                    tmp[x - 1][y - 1] = cnt - 1
        # 아래쪽 확산인 경우 
        if d == 3:
            if right(x, y, 0):
                if down(x, y + 1, 3):
                    q.append((x + 1, y + 1, cnt - 1))
                    tmp[x + 1][y + 1] = cnt - 1
            if down(x, y, 3):
                q.append((x + 1, y, cnt - 1))
                tmp[x + 1][y] = cnt - 1
            if left(x, y, 1):
                if down(x, y - 1, 3):
                    q.append((x + 1, y - 1, cnt - 1))
                    tmp[x + 1][y - 1] = cnt - 1
	# 바람 양 갱신 
    for i in range(r):
        for j in range(c):
            window[i][j] += tmp[i][j]

각 위치별 네가지 방향에 벽이 있는지 확인하는 함수는 다음과 같다. 

def up(x, y, i):
    if wall_row[x][y]:
        return False

    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        return True
    return False


def down(x, y, i):
    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        # 내려간 칸의 위쪽에 가로 벽이 존재하지 않으면
        if wall_row[nx][ny] != 1:
            return True
    return False

def left(x, y, i):
    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        # 왼쪽 칸의 오른쪽에 세로 벽이 존재하지 않으면
        if wall_col[nx][ny] != 1:
            return True
    return False

def right(x, y, i):
    if wall_col[x][y]:
        return False

    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        return True
    return False

3. 온도를 조절한다. 

온도 조절 역시 각 위치에서 벽이 없는 네가지 방향에 대해 온도차이를 계산하고 갱신해주면 된다. 동시에 발생하므로 마지막에 한꺼번에 해줘야 한다.

def adjust():
    tmp = [[0] * c for _ in range(r)]

    for x in range(r):
        for y in range(c):
            if right(x, y, 0):
                dif = window[x][y] - window[x][y + 1]

                if dif > 0:
                    ad = dif//4

                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x][y + 1] += ad

            if left(x, y, 1):
                dif = window[x][y] - window[x][y - 1]
                if dif > 0:
                    ad = dif // 4

                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x][y - 1] += ad

            if up(x, y, 2):
                dif = window[x][y] - window[x-1][y]
                if dif > 0:
                    ad = dif // 4
                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x - 1][y] += ad

            if down(x, y, 3):
                dif = window[x][y] - window[x+1][y]
                if dif > 0:
                    ad = dif // 4
                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x + 1][y] += ad

    for i in range(r):
        for j in range(c):
            window[i][j] += tmp[i][j]

4. 테두리 온도를 1도 씩 감소 시킨다. 

1번 씩 감소시켜야 하므로 중복되게 감소시키지 않게 주의한다. 

def decrease():
    for i in (0, r - 1):
        for j in range(c):
            if window[i][j] > 0:
                window[i][j] -= 1

    for j in (0, c - 1):
        for i in range(1, r - 1):
            if window[i][j] > 0:
                window[i][j] -= 1

 

전체 코드는 아래와 같다. 

from collections import deque

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


def up(x, y, i):
    if wall_row[x][y]:
        return False

    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        return True
    return False


def down(x, y, i):
    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        # 내려간 칸의 위쪽에 가로 벽이 존재하지 않으면
        if wall_row[nx][ny] != 1:
            return True
    return False

def left(x, y, i):
    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        # 왼쪽 칸의 오른쪽에 세로 벽이 존재하지 않으면
        if wall_col[nx][ny] != 1:
            return True
    return False

def right(x, y, i):
    if wall_col[x][y]:
        return False

    nx = x + dx[i]
    ny = y + dy[i]

    if 0 <= nx < r and 0 <= ny < c:
        return True
    return False

def spread(d, x, y):
    tmp = [[0] * c for _ in range(r)]
    q = deque()

    nx = x + dx[d]
    ny = y + dy[d]

    tmp[nx][ny] += 5
    q.append((nx, ny, 5))

    while q:
        x, y, cnt = q.popleft()

        if cnt < 1:
            break

            # 오른쪽
        if d == 0:
            if up(x, y, 2):
                if right(x - 1, y, 0):
                    q.append((x - 1, y + 1, cnt - 1))
                    tmp[x - 1][y + 1] = cnt - 1
            if right(x, y, 0):
                q.append((x, y + 1, cnt - 1))
                tmp[x][y + 1] = cnt - 1
            if down(x, y, 3):
                if right(x + 1, y, 0):
                    q.append((x + 1, y + 1, cnt - 1))
                    tmp[x + 1][y + 1] = cnt - 1
        # 왼쪽
        if d == 1:
            if up(x, y, 2):
                if left(x - 1, y, 1):
                    q.append((x - 1, y - 1, cnt - 1))
                    tmp[x - 1][y - 1] = cnt - 1
            if left(x, y, 1):
                q.append((x, y - 1, cnt - 1))
                tmp[x][y - 1] = cnt - 1
            if down(x, y, 3):
                if left(x + 1, y, 1):
                    q.append((x + 1, y - 1, cnt - 1))
                    tmp[x + 1][y - 1] = cnt - 1
        # 위쪽
        if d == 2:
            if right(x, y, 0):
                if up(x, y + 1, 2):
                    q.append((x - 1, y + 1, cnt - 1))
                    tmp[x - 1][y + 1] = cnt - 1
            if up(x, y, 2):
                q.append((x - 1, y, cnt - 1))
                tmp[x - 1][y] = cnt - 1
            if left(x, y, 1):
                if up(x, y - 1, 2):
                    q.append((x - 1, y - 1, cnt - 1))
                    tmp[x - 1][y - 1] = cnt - 1
        # 아래쪽
        if d == 3:
            if right(x, y, 0):
                if down(x, y + 1, 3):
                    q.append((x + 1, y + 1, cnt - 1))
                    tmp[x + 1][y + 1] = cnt - 1
            if down(x, y, 3):
                q.append((x + 1, y, cnt - 1))
                tmp[x + 1][y] = cnt - 1
            if left(x, y, 1):
                if down(x, y - 1, 3):
                    q.append((x + 1, y - 1, cnt - 1))
                    tmp[x + 1][y - 1] = cnt - 1

    for i in range(r):
        for j in range(c):
            window[i][j] += tmp[i][j]

def blow():
    for d, x, y in machine:
        spread(d, x, y)

def adjust():
    tmp = [[0] * c for _ in range(r)]

    for x in range(r):
        for y in range(c):
            if right(x, y, 0):
                dif = window[x][y] - window[x][y + 1]

                if dif > 0:
                    ad = dif//4

                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x][y + 1] += ad

            if left(x, y, 1):
                dif = window[x][y] - window[x][y - 1]
                if dif > 0:
                    ad = dif // 4

                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x][y - 1] += ad

            if up(x, y, 2):
                dif = window[x][y] - window[x-1][y]
                if dif > 0:
                    ad = dif // 4
                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x - 1][y] += ad

            if down(x, y, 3):
                dif = window[x][y] - window[x+1][y]
                if dif > 0:
                    ad = dif // 4
                    if ad > 0:
                        tmp[x][y] -= ad
                        tmp[x + 1][y] += ad

    for i in range(r):
        for j in range(c):
            window[i][j] += tmp[i][j]

def decrease():
    for i in (0, r - 1):
        for j in range(c):
            if window[i][j] > 0:
                window[i][j] -= 1

    for j in (0, c - 1):
        for i in range(1, r - 1):
            if window[i][j] > 0:
                window[i][j] -= 1

r, c, k = map(int, input().split())

graph = []
search = []
machine = []
wall_col = [[0] * c for _ in range(r)]
wall_row = [[0] * c for _ in range(r)]
window = [[0] * c for _ in range(r)]

for i in range(r):
    graph.append(list(map(int, input().split())))
    for j in range(c):
        if graph[i][j] == 5:
            search.append((i, j))
        elif graph[i][j] > 0:
            machine.append((graph[i][j] - 1, i, j))

w = int(input())

for _ in range(w):
    x, y, t = map(int, input().split())
    if t == 0:
        wall_row[x - 1][y - 1] = 1
    if t == 1:
        wall_col[x - 1][y - 1] = 1

cho_cnt = 0
while True:
    cho_cnt += 1

    blow()

    adjust()

    decrease()

    cnt = 0
    for x, y in search:
        if window[x][y] >= k:
            cnt += 1

    if cnt == len(search):
        print(cho_cnt)
        break

    if cho_cnt > 100:
        print(101)
        break