import sys
def input():
    return sys.stdin.readline().rstrip()

def check(arr):
    if max(arr) - min(arr) <=K:
        return False
    return True
def push():
    min_value = min(arr[-1])
    for i in range(N):
        if arr[-1][i] == min_value:
            arr[-1][i] += 1
def roll(arr):
    row,col = 1,1
    new_N = N
    time = 0
    while True:
        new_temp = [[-1 for _ in range(new_N-col)] for _ in range(row+1)]

        for y in range(col,new_N):
            new_temp[-1][y-col] = arr[-1][y]

        for y in range(col):
            for x in range(len(arr)):
                new_temp[y][len(arr)-x-1] = arr[x][y]
        new_N = new_N-col
        if time%2:
            row += 1
            col += 1
        time += 1
        arr = [row[:] for row in new_temp]
        row_N = len(new_temp)
        if row_N*(col+1) >N:
            break
    return arr
def outOfbound(x,y,row,col):
    if 0<=x<row and 0<=y<col:
        return False
    return True
def blow():
    row = len(new_arr)
    col = len(new_arr[0])
    temp = [[0 for _ in range(col)] for _ in range(row)]
    for x in range(row):
        for y in range(col):
            if new_arr[x][y] == -1:continue
            for i in range(4):
                nx = x + dx[i]
                ny = y + dy[i]
                if outOfbound(nx,ny,row,col):continue
                if new_arr[nx][ny] == -1:continue
                if new_arr[x][y] - new_arr[nx][ny] >=5:
                    gap = (new_arr[x][y] - new_arr[nx][ny])//5
                    temp[x][y] -= gap
                    temp[nx][ny] += gap
    for x in range(row):
        for y in range(col):
            new_arr[x][y] += temp[x][y]
def flatting(maze):
    temp_arr = [[]]
    row = len(maze)
    col = len(maze[0])
    for y in range(col):
        for x in range(row-1,-1,-1):
            if maze[x][y]==-1:continue
            temp_arr[-1].append(maze[x][y])
    return temp_arr
def spread():
    spread_arr = flatting(new_arr)
    temp = [[-1 for _ in range(N//4)] for _ in range(4)]

    for x in range(4):
        if x%2:
            start_x = N//4*x
            y = 0
            while y<N//4:
                temp[x][y] = spread_arr[-1][start_x+y]
                y += 1
        else:
            y = N//4-1
            if x == 2:
                start_x = 0
            else:
                start_x = N//4*2
            while y>=0:
                temp[x][y] = spread_arr[-1][start_x]
                start_x += 1
                y -= 1
    return temp
                
    
N,K = map(int,input().split())

arr = [list(map(int,input().split()))]
dx = [-1,0,1,0]
dy = [0,-1,0,1]
turn = 0
while check(arr[-1]):
    push()
    new_arr = roll(arr)
    blow()
    new_arr = spread()
    blow()
    arr = flatting(new_arr)
    turn += 1
print(turn)

 

 

삼성 기출 문제들 중에서 푸는데 제일 오래걸렸던 문제였다.

 

패턴은 찾았는데, 그걸 코드로 옮기는데 힘들었다.

 

두가지 부분이 힘들었는데 돌돌 마는 부분이랑

 

그리고 마지막에 4부분을 나눠서 합치는 부분이다.

 

이 부분엗 대한 패턴을 찾고, 그걸 코드로 옮겨주면 되는 문제였다.

 

내가 찾은 패턴은

 

돌돌 마는 패턴에서는

 

처음에

 

row 1 col1

row2 col1

row2 col2

row3 col2

row3 col3

 

접혀진 부분의 크기가 이와 같음을 알 수 있다.

 

또한 이때 다음번의 영향은 그 전의 col만큼 이후것들은 제일 밑단에 감을 알 수 있고

 

원래 말려있던 부분들은 오른쪽으로 90도 회전을 함을 알 수 있다.

 

이런 방식으로 해서 구해주면 된다.

 

 

마지막으로 4부분으로 나눠서 합치는 부분은 예제 그림을 잘 보면 패턴을 찾을 수 있다.

 

위치가 어떻게 되어있던지

 

 

1 2 3 4 5 6 7 8 9 .... 4N

 

4N 이라는 길이의 array가 있으면

 

3N 3N-1 3N-2 .... 2N+1

N+1 N+2 .... 2N

N N-1 N-2 ....    1

3N+1 3N+2 .... 4N

 

처럼 접히는것을 알 수 있고, 이걸 구현해주면 된다.

 

이 외에는 이번 삼성기출에서 풀었던 것처럼 해주면 된다.

 

이 풀이 자체는 좀 난잡하게 푼 문제라서, python으로 푸시는 분들은

 

jh05013님의 풀이를 참조하시는걸 추천드립니다.

import sys
from collections import deque
def input():
    return sys.stdin.readline().rstrip()

def bfs(x,y,cnt):
    score = 0
    visited = [[False for _ in range(M)] for _ in range(N)]
    index_arr[x][y] = cnt
    visited[x][y] =True
    queue = deque()
    queue.append((x,y))
    stan = arr[x][y]
    while queue:
        x,y = queue.popleft()
        score += arr[x][y]

        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0<=nx<N and 0<=ny<M:
                if arr[nx][ny] == stan and not visited[nx][ny]:
                    visited[nx][ny] = True
                    index_arr[nx][ny] = cnt
                    queue.append((nx,ny))
    return score


def roll(direction):
    global dice
    if direction == 0:
        dice[0], dice[2], dice[3], dice[5] = dice[3], dice[0], dice[5], dice[2]
    elif direction == 1:
        dice[0], dice[1], dice[4], dice[5] = dice[1], dice[5], dice[0], dice[4]
    elif direction == 2:
        dice[0], dice[2], dice[3], dice[5] = dice[2], dice[5], dice[0], dice[3]
    else:
        dice[0] , dice[1] , dice[4], dice[5] = dice[4], dice[0], dice[5], dice[1]
def innerOfBound(x,y):
    if 0<=x<N and 0<=y<M:
        return True
    return False
def move(dire):
    x,y = dice_position
    nx = x + dx[dire]
    ny = y + dy[dire]
    if innerOfBound(nx,ny):
        return [(nx,ny),dire]
    dire = (dire+2)%4
    nx = x + dx[dire]
    ny = y + dy[dire]
    return [(nx,ny),dire]

def get_score(position):
    x,y = position
    return sum_list[index_arr[x][y]]
def curve(dire,position):
    x,y = position
    if dice[-1] > arr[x][y]:
        return (dire+1)%4
    elif dice[-1] < arr[x][y]:
        return (dire-1)%4
    else:
        return dire
N,M,K = map(int,input().split())

arr = [list(map(int,input().split())) for _ in range(N)]
dx = [0,1,0,-1]
dy = [1,0,-1,0]
index_arr = [[-1 for _ in range(M)] for _ in range(N)]
sum_list = []
cnt = 0
for x in range(N):
    for y in range(M):
        if index_arr[x][y] == -1:
            sum_list.append(bfs(x,y,cnt))
            cnt += 1

dice = [1,2,3,4,5,6]
dire = 0
# 동,남,서,북
# [4,2,1,6,5,3]
# [2,6,3,4,1,5]
# [3,2,6,1,5,4]
# [5,1,3,4,6,2]
dice_position = [0,0]
result = 0
while K>0:

    dice_position,dire = move(dire)
    roll(dire)
    result += get_score(dice_position)
    dire = curve(dire,dice_position)
    K -= 1
print(result)

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

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지

www.acmicpc.net

 

이 문제를 풀었던 사람이라면 쉽게 풀 수 있었던 문제인것 같다.

 

여기서 시간적으로 아끼자면, 단 1번의 bfs로 각 위치의 점수를 미리 구해두면 편하다.

 

그 외에는 문제에서 시키는 대로 회전하고 방향을 바꿔주면 되는 문제였다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 2240 자두나무  (0) 2021.12.04
[BOJ/백준] 23291 어항정리  (0) 2021.11.07
[BOJ/백준] 23290 마법사 상어와 복제  (0) 2021.10.26
[BOJ/백준] 23289 온풍기 안녕!  (0) 2021.10.25
[BOJ/백준] 1958 LCS 3  (0) 2021.09.28
import sys
def input():
    return sys.stdin.readline().rstrip()

def make_aqua():
    return [[[0 for _ in range(8)] for _ in range(4)] for _ in range(4)]
def outOfBound(x,y):
    if 0<=x<4 and 0<=y<4:
        return False
    return True

def move_fish(arr,T):
    new_aqua = make_aqua()
    for x in range(4):
        for y in range(4):
            for d in range(8):
                if arr[x][y][d]:
                    copy_d = d
                    for _ in range(8):
                        nx = x + dx[copy_d]
                        ny = y + dy[copy_d]
                        if outOfBound(nx,ny) or (nx,ny) == sharks or smells[nx][ny] >= T-2:
                            copy_d = (copy_d-1)%8
                        else:
                            new_aqua[nx][ny][copy_d] += arr[x][y][d]
                            break
                    else:
                        new_aqua[x][y][d] += arr[x][y][d]
    return new_aqua

def shark_swim(x,y,move,shark_dict,kill,visited):
    if len(move) == 3:
        shark_dict[move] = kill
        return
    else:
        for i in range(1,5):
            nx = x + shark_dx[i]
            ny = y + shark_dy[i]
            if outOfBound(nx,ny):continue
            if (nx,ny) not in visited:
                new_kill = sum(aqua[nx][ny])
                visited.add((nx,ny))
                shark_swim(nx,ny,move+str(i),shark_dict,kill + new_kill,visited)
                visited.remove((nx,ny))
            else:
                shark_swim(nx,ny,move+str(i),shark_dict,kill,visited)
def sharks_move(sharks,T):
    x,y = sharks
    shrks_dict = {}
    shark_swim(x,y,'',shrks_dict,0,set())
    move_sort = sorted(shrks_dict.keys(),key= lambda x : (-shrks_dict[x],x))
    for d in move_sort[0]:
        d = int(d)
        nx = x + shark_dx[d]
        ny = y + shark_dy[d]
        for i in range(8):
            if aqua[nx][ny][i]:
                aqua[nx][ny][i] = 0
                smells[nx][ny] = T
        x,y = nx,ny
    return x,y



N,S = map(int,input().split())
aqua = make_aqua()

for _ in range(N):
    x,y,d = map(int,input().split())
    aqua[x-1][y-1][d-1] += 1

dx = [0,-1,-1,-1,0,1,1,1]
dy = [-1,-1,0,1,1,1,0,-1]
shark_dx = [0,-1,0,1,0]
shark_dy = [0,0,-1,0,1]
smells = [[-float('inf') for _ in range(4)] for _ in range(4)]
sharks = tuple(map(lambda x: x-1,map(int,input().split())))
time = 0
while time<S:
    copy_aqua = [[col[:] for col in row] for row in aqua]
    aqua = move_fish(aqua,time)
    sharks = sharks_move(sharks,time)
    for x in range(4):
        for y in range(4):
            for d in range(8):
                aqua[x][y][d] += copy_aqua[x][y][d]

    time += 1

result = 0

for x in range(4):
    for y in range(4):
        result += sum(aqua[x][y])
print(result)

 

이 문제는 문제를 읽어보면 물고기의 개개로 움직이게 되면, 시간의 소모가 많이 되는것을 알 수 있다.

 

만약 물고기가 100만개를 일일히 전부 움직이고

 

최악의 경우를 생각해보면 1턴마다 100만*8의 이동이 되게 되고, 그것이 최대 100턴 지속되면 1억을 넘어가게 되서 시간초가가 날수도 있는 점을 생각해보면, 이 문제는 물고기의 위치와 방향에 따라 한꺼번에 움직이는 방식으로 해결해야함 을 알 수 있다.

 

먼저 4*4*8 크기의 aqua라는 수족관 배열을 만들어놓는다. 이 배열안에 입력으로 주어진 물고기의 위치와 방향에 맞게

 

넣어주면 된다.

 

이 문제도 삼성 기출 대부분이 그런것처럼 각 단계별이 있고, 그 단계에 맞게 시키는 것을 진행하면 된다.

 

그렇기 때문에 이 문제에서도 최대한 단계별에 맞게, 함수로 만들어서 진행을 했다.

 

1단계는 현재 수족관에 있는 물고기에게 복제 마법을 하는 것이다.

 

그렇다는 것은 현재 aqua에 있는 물고기 정보를 어디에 저장을 해야하기 때문에, 나는 copy_aqua라는 배열에,

 

현재 aqua의 물고기 정보를 저장해두었다.

 

여기서 deepcopy를 써도 되지만, 시간적으로 더 늦게 복사되기 때문에 list comprehension를 통해 복사를 해두었다.

 

2단계는 물고기가 움직이는 단계이다. 

 

물고기는 범위를 벗어나서도 안되고, 상어와 위치가 겹쳐서도 안되며, 물고기의 냄새가 있으면 안된다고 되어있다.

 

그리고 움직일 방향이 있을때까지 반시계 45도 방향으로 회전을 하며, 만약에 움직일 방향이 없으면 그 자리에 있는다.

 

라는 조건이 있다.

 

이 이동을 해결한 방법은 다음과 같다. 현재 물고기의 위치 x,y 와 방향 d가 있다하면,

 

우리는 문제에서 주어진 1~8의 이동방향이 숫자가 늘어남에 따라 시계방향으로 회전한다는 것을 알 수 있다.

 

그러면 우리는 반시계 45도 방향이라는 것은 주어진 이동방향 1~8을 각각 0~7로 변화시켰을때, 

 

(현재 이동방향-1)%8 을 하면, 반시계 45도 회전한 이동방향이 나옴을 알 수 있다.

 

그러면 우리는 총 8번의 반복을 하면서 범위를 벗어나거나, 상어가 존재하거나, 물고기 시체가 있을때에는

 

(이동방향 -1)%8을 해주면서 물고기를 계속 회전 시켜주면 된다는 것을 알 수 있다.

 

여기서 주의해야할 점은 원본의 이동방향은 그대로 냅두어야하기 때문에, copy_d라는 변수를 만들어 회전을 담당하는 변수를 새로 만들어줬다.

 

이렇게 해서 위의 3조건에 만족하지 않는 공간이 있을때에는 새 수족관에 더해주면 된다.

 

그리고 여기서도 for else문을 통해 8번의 방향전환동안, 물고기가 움직일 공간이 없을때에는,

 

현재 위치에 더해주는 로직을 해주었다.

 

여기서 중요한 물고기 시체와 관련된 사항은 밑에서 적도록 하겠다.

 

 

3번째 단계는 상어가 이동하는 단계이다.

 

상어는 상하좌우로 인접한 곳으로 이동을 하며, 이동하는 도중에 격자 범위를 벗어나는 이동은 무시한다고 되어있다.

 

이 상어 이동은 재귀를 통해 구현을 했으며, 재귀를 함에 있어서 주의해야할 점은

 

한번 물고기 먹은 곳을 당신 방문했을 때, 물고기를 먹는것을 중복해서 하지 않도록 방문 표시를 제대로 해주면 된다.

 

 

이렇게 상어의 모든 이동에 대해, 판별을 해준뒤 sort를 통해 물고기를 가장 많이 먹으면서,

 

이동방향이 사전순이 녀석으로 이동을 시켜준다.

 

여기서 물고기의 시체를 남겨두는 작업을 해야하는데, 이 물고기의 시체는 2턴뒤에 사라져야한다.

 

이 것을 따로 하나하나 초기화 하기에는 복잡하다고 생각되어,

 

4*4 크기의 smells라는 배열을 만들고, 거기에 물고기 시체가 생긴 턴을 적어두는 것으로 문제점을 해결했다.

 

 

smells[x][y] >= T- 2

 

특정위치의 냄새의 값이 현재턴에서 -2 한 값보다 크거나 같다는 것은 1턴전 혹은 2턴전에 물고기 시체가 되었다는 것을 의미하게 되고,

 

이렇게 표시하는 것만으로 자동적으로 2턴이 지난후에는 냄새가 사라지는것처럼 구현을 할 수 있다는 장점이 있다.

 

마지막으로 우리가 제일 첫번째에 해놓았던, copy_aqua를 원래 aqua에 더해주는 것으로

 

한 턴을 마무리 하면 된다.

 

문제에 주어진 턴을 모든 소모한뒤에 aqua에 남아있는 물고기의 수를 구해서 출력해주면 된다.

 

이 문제에서 주의해야했던 점은 몇가지가 있는데,

 

물고기의 시체가 2턴마다 사라지는 것을 어떻게 구현할 것인가와

 

상어의 이동방향은 어떻게 구현할것인가

 

그리고 물고기의 이동은 어떻게 구현할 것인가이다.

 

였다. 이 3부분에 대한 효율적으로 로직을 짜야했던 문제였다.

 

이 문제를 풀 때 1시간 30분이 걸렸는데, 30분동안 디버깅한게 tuple하고 list를 비교연산자로 비교해서 틀렸던 거였다..

 

list와 tuple은 타입이 다른 것을 다시 깨닫게 되었다.. 망할

 

 

 

def shark_swim(x,y,move,kill,visited):
    if len(move) == 3:
        return (kill,move)
    else:
        temp = (0,'000')
        for i in range(1,5):
            nx = x + shark_dx[i]
            ny = y + shark_dy[i]
            if outOfBound(nx,ny):continue
            if (nx,ny) not in visited:
                new_kill = sum(aqua[nx][ny])
                visited.add((nx,ny))
                temp = max(temp,shark_swim(nx,ny,move+str(i),kill + new_kill,visited))
                visited.remove((nx,ny))
            else:
                temp = max(temp,shark_swim(nx,ny,move+str(i),kill,visited))
        return temp

def sharks_move(sharks,T):
    x,y = sharks
    _,max_move = shark_swim(x,y,'',0,set())
    for d in max_move:
        d = int(d)
        nx = x + shark_dx[d]
        ny = y + shark_dy[d]
        for i in range(8):
            if aqua[nx][ny][i]:
                aqua[nx][ny][i] = 0
                smells[nx][ny] = T
        x,y = nx,ny
    return x,y
    
    
shark_dx = [0,0,1,0,-1]
shark_dy = [0,1,0,-1,0]

dict를 사용하지 않고, 재귀를 통해 max값을 되돌려주는 방식으로도 풀수 있다.

 

튜플로 max를 비교할수 있기 때문에 가능한 것이고, 이때는 앞에서는 1,2,3,4에 맞춰서 상좌우하를 만들었는데, 여기서는 반대로 더 높은숫자가 나올수 있도록 상좌우하를 4,3,2,1에 매핑을 시켜두었다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 23291 어항정리  (0) 2021.11.07
[BOJ/백준] 23288 주사위 굴리기 2  (0) 2021.10.27
[BOJ/백준] 23289 온풍기 안녕!  (0) 2021.10.25
[BOJ/백준] 1958 LCS 3  (0) 2021.09.28
[BOJ/백준] 2146 다리 만들기  (0) 2021.09.03
import sys
from collections import defaultdict,deque
def input():
    return sys.stdin.readline()
def outOfBound(x,y):
    if 0<=x<N and 0<=y<M:
        return False
    return True
def check(position):
    for x,y in position:
        if arr[x][y] <K:
            return True
    return False
def blocking(a,b):
    if block_air[a].get(b):
        return True
    return False
def air_dire(dir):
    swap_dir = [[],[3,4],[3,4],[1,2],[1,2]]
    return [[dir],[swap_dir[dir][0],dir],[swap_dir[dir][1],dir]]
def calcurate(conditioner):
    air = [[0 for _ in range(M)] for _ in range(N)]
    for x,y in conditioner:
        dir = conditioner[(x,y)]
        x = x + dx[dir]
        y = y + dy[dir]
        air[x][y] += 5
        visited = set()
        visited.add((x,y))
        queue = deque()
        queue.append((x,y,5))

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

            for total_dir in air_dire(dir):
                cx,cy = x,y
                for i in total_dir:
                    nx = cx + dx[i]
                    ny = cy + dy[i]
                    if outOfBound(nx,ny):break
                    if blocking((cx,cy),(nx,ny)):break
                    cx,cy = nx,ny
                else:
                    if (cx,cy) not in visited:
                        visited.add((cx,cy))
                        air[cx][cy] = air[cx][cy] + cnt -1
                        if cnt >2:
                            queue.append((cx,cy,cnt-1))
    return air
def blow():
    temp = [[0 for _ in range(M)] for _ in range(N)]
    for x in range(N):
        for y in range(M):

            for i in range(1,5):
                nx = x + dx[i]
                ny = y + dy[i]
                if outOfBound(nx,ny):continue
                if blocking((x,y),(nx,ny)):continue
                if arr[x][y] >= arr[nx][ny] + 4:
                    gap = (arr[x][y] - arr[nx][ny])//4
                    temp[x][y] -= gap
                    temp[nx][ny] += gap

    for x in range(N):
        for y in range(M):
            arr[x][y] += temp[x][y]
            if arr[x][y]<0:
                arr[x][y] = 0
def down():
    for x in range(N):
        for y in range(M):
            if x == 0 or y == 0 or y == M-1 or x == N-1:
                if arr[x][y]:
                    arr[x][y] -= 1
N,M,K = map(int,input().split())

arr = [[0 for _ in range(M)] for _ in range(N)]
dx = [0,0,0,-1,1]
dy = [0,1,-1,0,0]
air_conditioner = {}
check_position = []
for x in range(N):
    temp = list(map(int,input().split()))
    for y in range(M):
        if temp[y] and temp[y]<5:
            air_conditioner[(x,y)] = temp[y]
        elif temp[y] == 5:
            check_position.append((x,y))

W = int(input())
block_air = defaultdict(dict)
for _ in range(W):
    x,y,c = map(int,input().split())
    x -= 1
    y -= 1
    if c == 0:
        block_air[(x,y)][(x-1,y)] = 1
        block_air[(x-1,y)][(x,y)] = 1
    else:
        block_air[(x,y)][(x,y+1)] = 1
        block_air[(x,y+1)][(x,y)] = 1
temperature_arr = calcurate(air_conditioner)

time = 0
while check(check_position) and time<=100:
    
    for x in range(N):
        for y in range(M):
            arr[x][y] += temperature_arr[x][y]
    blow()
    down()
    time += 1
print(time)

하반기 삼성 기출 문제라서 한번 풀어봤다.

 

삼성 기존 문제들이 그랬던 것처럼 시키는 대로 시뮬레이션을 돌면 되는 문제였다.

 

문제를 읽어보면 1턴에 차례대로 일정한 순서대로 한다.

 

1. 집에 있는 모든 온풍기에서 바람이 한번 나옴

2. 온도가 조절됨

3. 바깥의 온도가 1씩 감소

4. 1턴이 증가하고, 조사하는 모든 칸이 K도 이상이면 종료, 아니면 다시 처음부터 시작

 

총 4단계로 나뉘어져있고, 이걸 무한히 반복하면 된다.

 

그러면 이 문제를 풀기 위한 사전조건들을 먼저 구해주고, 각 단계별로 함수로 나누어서, 진행을 하면 된다.

 

그리고 이 문제를 풀면서 어려운 변수가 되는 것은 벽을 어떻게 판별을 헐것이냐 이다.

 

이 문제는 2차원 dict를 통해 해결를 했다. 문제에서 벽은 총 2종류로 가로로 세워진 벽, 세로로 세워진 벽이다.

 

그러면 이 벽을 어떻게 판별을 할것인가가 고민이었다. 이 문제는 a라는 위치에서 b라는 위치를 갈때 벽이 있느냐를 판별하면 되므로 2차원 dict를 통해 문제를 해결했다.

c가 0이라면, (x,y) 와 (x-1,y)를 키를 하는 dict를 만들어두고, 우리가 이동을 할때 해당 키가 존재하는지 유무를 통해, 벽이 존재하는지 안하는지를 판별할 수 있게 된다.

c가 1일때에도 동일하게 (x,y)와 (x,y+1)를 키로 하는 dict를 만들어주면 된다.

 

이렇게 먼저 사전작업을 해준뒤 위의 단계를 진행해주면 된다.

 

1번 로직인 집에 있는 모든 온풍기에서 바람이 1번 나옴은, 문제에서 주어진 것처럼 매턴 마다 일정한 값이, 일정한 칸에 더해지는 걸 알 수 있다.

 

그러므로, 먼저 clacurate 라는 함수를 통해, 모든 온풍기들이 바람이 불었을 때, 늘어나는 온도들을 미리 구해놓았다.

 

이렇게 1번 구해놓으면 N*M 을 반복하면서 원래 arr에 더해주면 끝이기 때문이다.

 

온풍기가 퍼지는 방향을 보면, 온풍기가, 동,서 방향으로 있다면, 동 서 방향으로 한칸씩 전진을 하며,

 

대각선 방향으로 진행될 때에는 먼저, 북이나 남 방향으로 퍼진뒤 동서 방향으로 진행을 하게 된다.

 

이 점을 이용하여, air_dire라는 함수를 통해, 온풍기가 진행하는 방향들을 먼저 구해주면 된다. 총 3가지 방향으로,

 

온풍기의 방향대로 나아가는 것과, 온풍기가 바라보는 방향의 90도 방향으로 먼저 꺾은뒤

 

온풍기가 바라보는 방향으로 진행을 하는 총 3가지의 방향을 한다.

 

위와 같이 준비를 한뒤에 bfs를 돌려주면 된다. 이 바람이 범위를 벗어나는지, 벽에 가로막혔는지를 판별을 하고, 

 

cnt가 2까지만 돌려주면 된다.

 

여기서 python의 for else 문을 사용할줄 알면 쉽게 트리거를 구분할 수 있다.

 

 

이렇게 우리는 calcurate를 통해, 우리는 온풍기가 켜졌을 때, 온도의 변화를 미리 구할 수 있게 되었다.

 

 

2번 로직은 인제 온도차를 통해, 온도가 조절되는 것을 구현을 해야한다. 이 부분도 비슷하게, N*M의 각 지점마다,

 

상하좌우를 탐색한 뒤, 벽으로 막혀있는지, 범위를 벗어나는지 먼저 판별을 하고 난뒤에,

 

인접한 위치의 온도가 현재 위치보다 온도가 낮으면, 그 온도의 gap을 4로 나눈 몫을 temp에 각각 마킹을 해준다.

 

온도가 높았던 곳은 - 온도가 낮았던 곳은 +를 해준다.

 

이렇게 모든 작업을 마친 뒤, 원본 배열에 더해주는 작업을 하고, 혹시 모르니, 음수는 0으로 바꿔주는 로직을 넣어줬다.

 

 

우리는 이렇게 크게 1,2번 로직을 완성했고, 인제 3,4번 로직을 구해놓으면 된다.

 

다음으로 3번 로직은 제일 외부의 온도가 1도씩 줄어든다고 했는데 사실 이건 구하기 쉬운 문제이다.

 

N*M 만큼 모든 점을 돌아다니면서 x가 0이거나 x가 N-1이거나 y가 0이거나 M-1일때에가 제일 외부인 걸 알 수 있다.

 

그리고 해당 위치의 온도가 1도이상 일때 온도를 1도씩 줄여주면 된다.

 

 

마지막으로 4번은 조사하는 부분들을 처음 입력이 들어왔을때, 따로 빼놓고,

 

이걸 반복문을 돌면서 한 곳의 온도가 K 미만이면, True 모든 곳의 온도가 K이상이면 False를 반환하는 check 함수를 만들어두었다.

 

그리고 모든 턴의 횟수가 101이 넘어가면 101로 출력해야하므로, time이 100이하일떄까지만 돌아가도록 while문을 작성해놓았다.

 

이 문제에서 크게 고민해야했던 부분은 3가지이다.

 

첫번째로, 1번 로직은 매번 반복되는 것이므로, 한번만 구해놓으면, 그 온도 변화량을 다시 구할 필요없이, 이용을 하면 된다.

 

두번째는 벽을 어떻게 구현을 할것인지 하는 것이다. 나는 이 문제를 풀때 두 튜플의 key값으로 하는 dict를 통해 만들어 뒀는데, 다른 방법들도 있을 것이다. 이 벽을 판별하는 것이 이 문제의 중요 포인트 중 하나였다.

 

그리고 마지막으로, 온풍기의 대각선 이동을 어떻게 구현을 하는것이냐였다.

 

온풍기의 진행방향에 벽이 세워져 있다하더라도, 온풍기의 90도 되는 방향에 벽이 없으면,

 

그 벽을 통해, 다음 칸으로 진행을 할수도 있었다. 그렇기 때문에, 온풍기의 대각선 이동방향을 어떻게 구현 할것인지가

 

꽤나 고민을 해야했던 부분이였다.

 

이 문제는 각각의 로직 별로, 함수를 만들어놓고, 그 함수 내에서 한가지 기능을 완료하는 식으로 해야,

 

어떤 부분에 에러가 발생했을때, 금방 알아차릴 수 있는 문제였다.

 

 

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 23288 주사위 굴리기 2  (0) 2021.10.27
[BOJ/백준] 23290 마법사 상어와 복제  (0) 2021.10.26
[BOJ/백준] 1958 LCS 3  (0) 2021.09.28
[BOJ/백준] 2146 다리 만들기  (0) 2021.09.03
[BOJ/백준] 1943 동전 분배  (0) 2021.09.03
import sys

def input():
    return sys.stdin.readline().rstrip()

def fishing(y):
    for x in sorted(shark_dict[y].keys()):
        if shark_dict[y][x]:
            temp = shark_dict[y][x][2]
            del shark_dict[y][x]
            return temp
    return 0
R,C,M = map(int,input().split())

shark_dict = [{} for _ in range(C)]
for _ in range(M):
    x,y,speed,dire,size = map(int,input().split())
    x -= 1
    y -= 1
    dire -= 1
    shark_dict[y][x] = [dire,speed,size]

result = 0
dx = [-1,1,0,0]
dy = [0,0,1,-1]
reverse = [1,0,3,2]
for y in range(C):
    remain_cnt = 0
    move_shark_dict = [{} for _ in range(C)]
    result += fishing(y)
    for shark_y in range(C):
        for shark_x in shark_dict[shark_y]:
            dire, speed, size = shark_dict[shark_y][shark_x]
            nx = shark_x
            ny = shark_y
            if dire in [0,1]:
                for _ in range(speed):
                    nx += dx[dire]
                    if nx >= R-1 or nx <= 0:
                        dire = reverse[dire]
                        if nx >= R:
                            nx = R - 2
                        elif nx<0:
                            nx = 1

            else:
                for _ in range(speed):
                    ny += dy[dire]
                    if ny >= C-1 or ny <= 0:
                        dire = reverse[dire]
                        if ny >=C:
                            ny = C-2
                        elif ny<0:
                            ny = 1
            if move_shark_dict[ny].get(nx):
                if move_shark_dict[ny][nx][2] < size:
                    move_shark_dict[ny][nx] = [dire, speed, size]
            else:
                move_shark_dict[ny][nx] = [dire,speed,size]
                remain_cnt += 1
    if remain_cnt:
        shark_dict = [{} for _ in range(C)]

        for y in range(C):
            for x in move_shark_dict[y]:
                shark_dict[y][x] = move_shark_dict[y][x]
    else:
        break

print(result)

문제에서 정해준대로 구현과 시뮬레이션을 정직하게 한 방법이다.

 

즉 이동도 한칸 한칸씩 이동을 해서 통과는 했지만 시간은 오래 걸리는 풀이 방법이다.

 

 

import sys
def input():
    return sys.stdin.readline().rstrip()

def fishing(y):
    for x in sorted(shark_dict[y].keys()):
        if shark_dict[y][x]:
            temp = shark_dict[y][x][2]
            del shark_dict[y][x]
            return temp
    return 0
R,C,M = map(int,input().split())

shark_dict = [{} for _ in range(C)]
for _ in range(M):
    x,y,speed,dire,size = map(int,input().split())
    x -= 1
    y -= 1
    dire -= 1
    shark_dict[y][x] = [dire,speed,size]

result = 0
dx = [-1,1,0,0]
dy = [0,0,1,-1]
reverse = [1,0,3,2]
for y in range(C):
    remain_cnt = 0
    move_shark_dict = [{} for _ in range(C)]
    result += fishing(y)

    for shark_y in range(C):
        for shark_x in shark_dict[shark_y]:
            dire, speed, size = shark_dict[shark_y][shark_x]
            nx = shark_x + dx[dire]*speed
            ny = shark_y + dy[dire]*speed
            if dire in [0,1]:
                while not(0<=nx<R):
                    if nx<0:
                        nx = -nx
                        dire = reverse[dire]
                    else:
                        nx = (R-1)-(nx-(R-1))
                        dire = reverse[dire]
            else:
                while not(0<=ny<C):
                    if ny<0:
                        ny = -ny
                        dire = reverse[dire]
                    else:
                        ny = (C-1)-(ny-(C-1))
                        dire = reverse[dire]

            if move_shark_dict[ny].get(nx):
                if move_shark_dict[ny][nx][2] < size:
                    move_shark_dict[ny][nx] = [dire, speed, size]
            else:
                move_shark_dict[ny][nx] = [dire,speed,size]
                remain_cnt += 1
    if remain_cnt:
        shark_dict = [{} for _ in range(C)]

        for y in range(C):
            for x in move_shark_dict[y]:
                shark_dict[y][x] = move_shark_dict[y][x]
    else:
        break

print(result)

두번째 풀이는 방향을 한번에 구하는 것을 통해 시간을 아낄수 있었다. 정확히 말하면 한번은 아니지만, 한칸한칸씩 움직이는 것보다 빠르게 만들었다.

 

 

import sys
def input():
    return sys.stdin.readline().rstrip()

def fishing(y):
    for x in range(R):
        if pos[x][y]:
            size = sharks[pos[x][y]][4]
            del sharks[pos[x][y]]
            pos[x][y] = 0
            return size
    return 0
R,C,M = map(int,input().split())

sharks = {}

pos = [[0 for _ in range(C)] for _ in range(R)]
for num in range(1,M+1):
    x,y,speed,dire,size = map(int,input().split())
    x -= 1
    y -= 1
    dire -= 1
    sharks[num] = (x,y,speed,dire,size)
    pos[x][y] = num


result = 0
dx = [-1,1,0,0]
dy = [0,0,1,-1]
reverse = [1,0,3,2]
for y in range(C):
    if len(sharks)==0:
        break
    result += fishing(y)
    next_pos = [[0 for _ in range(C)] for _ in range(R)]
    eaten = []
    for num in sharks:
        shark_x,shark_y,speed,dire,size = sharks[num]
        nx = shark_x + dx[dire]*speed
        ny = shark_y + dy[dire]*speed
        if dire in [0,1]:
            while not(0<=nx<R):
                if nx<0:
                    nx = -nx
                    dire = reverse[dire]
                else:
                    nx = (R-1)-(nx-(R-1))
                    dire = reverse[dire]
        else:
            while not(0<=ny<C):
                if ny<0:
                    ny = -ny
                    dire = reverse[dire]
                else:
                    ny = (C-1)-(ny-(C-1))
                    dire = reverse[dire]


        if next_pos[nx][ny] != 0:
            prev_shark_num = next_pos[nx][ny]
            if sharks[prev_shark_num][4] < size:
                next_pos[nx][ny] = num
                sharks[num] = (nx,ny,speed,dire,size)
                eaten.append(prev_shark_num)
            else:
                eaten.append(num)

        else:
            next_pos[nx][ny] = num
            sharks[num] =  (nx,ny,speed,dire,size)
    for num in eaten:
        del sharks[num]
    pos = [row[:] for row in next_pos]

print(result)

이 풀이는 상어의 정보를 저장하는 딕셔너리와 상어의 위치를 알 수 있는 리스트를 통해 관리를 해서 푸는 방식이다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 19535 ㄷㄷㄷㅈ  (0) 2021.09.03
[BOJ/백준] 18513 쉼터  (0) 2021.09.03
[BOJ/백준] 17090 미로 탈출하기  (0) 2021.09.03
[BOJ/백준] 3108 로고  (0) 2021.09.03
[BOJ/백준] 14938 서강그라운드  (0) 2021.09.02
import sys
from collections import deque
def input():
    return sys.stdin.readline().rstrip()

def pop(queue,flag):
    if flag:
        return queue.pop()
    else:
        return queue.popleft()
T = int(input())

for _ in range(T):
    commands = list(input())
    N = int(input())
    if N:
        arr = deque(input().replace('[','').replace(']','').split(','))
    else:
        input()
        arr = deque()
    # 0 정방향 1 역방향
    flow = 0
    flag = True
    for command in commands:
        if command == 'R':
            flow = (flow+1)%2
        else:
            if arr:
                pop(arr,flow)
            else:
                flag = False
                break
    if flag:
        if flow:
            arr.reverse()
        print(f'[{",".join(arr)}]')
    else:
        print('error')

 

이 문제는 deque를 통해, 역방향인지 정방향인지에 따라 나눠서 결정을 지으면 되는 문제였다.

 

정방향인지 역방향인지에 따라 pop을 하는 위치를 바꿔주고, 최종 결과를 출력을 할때에도, 구분을 해줬다.

import sys
import math
def input():
    return sys.stdin.readline().rstrip()

def check(max_hp):
    cu_atk = input_atk
    cu_hp = max_hp
    for command,atk,hp in arr:
        if command == 1:
            atk_cnt = math.ceil(hp/cu_atk)
            cu_hp -= (atk_cnt-1)*atk
            if cu_hp<=0:
                return False
        else:
            cu_atk += atk
            cu_hp += hp
            if cu_hp> max_hp:
                cu_hp = max_hp
    return True

N,input_atk = map(int,input().split())

arr = []


for _ in range(N):
    t,a,h = map(int,input().split())

    arr.append((t,a,h))

left = 0
right = 999999000001*123456


while left+1<right:
    mid = (left+right)//2
    if check(mid):
        right = mid
    else:
        left = mid
print(right)

 

첫 풀이는 전형적인 이분탐색 풀이이다.

 

올림을 통해 적 hp를 현재 용사의 공격력으로 나눈 몫을 올림을 해서 용사의 공격횟수를 구했다.

 

그리고 마지막 공격으로 적이 죽었을때에는 공격을 맞지 않으므로, 용사의 공격횟수에서 -1을 하고

 

몬스터의 공격력 만큼 곱해서 용사의 hp를 줄여준다.

 

이때 0보다 이하이면, False를 반환해준다.

 

그리고 물약이 있는곳이 생기면 용사의 공격력을 올려주고, 설정된 최대피를 넘어가게 회복되면 최대피로 바꿔주면 된다.

 

이런식으로 해서 용사가 죽지않고 던전을 돌파할 수 있는 최소 피를 구하면 된다.

 

import sys
import math
def input():
    return sys.stdin.readline()


N,user_atk = map(int,input().split())


max_hp = 0
acc_hp = 0
# 데미지는 마이너스
# 힐은 플러스
damage_or_heal = 0


arr = [list(map(int,input().split())) for _ in range(N)]


for command,atk,hp in arr:
    if command == 1:
        atk_cnt = math.ceil(hp/user_atk)
        damage_or_heal = -(atk_cnt-1)*atk
    else:
        user_atk += atk
        damage_or_heal = hp
    
    acc_hp += damage_or_heal
    if acc_hp>0:
        acc_hp = 0
    max_hp = max(max_hp,abs(acc_hp))
print(max_hp+1)

 두번째 풀이는 신박한 풀이였다.

 

최대 누적 데미지를 계산을 해서 푸는 방식이였다.

 

이 방식은 단 한번의 반복문으로 구할 수 있으므로, 윗 방식보다 더 빠른 방식이다.

 

최대 누적데미지를 구하는 방식은 다음과 같다.

 

damage_or_heal은 용사가 받은 데미지나 heal을 나타내는 것이다.

 

데미지이면 마이너스값을 받고, heal이면 플러스 값을 받는다.

 

max_hp는 우리가 구할 최대 hp이다.

 

acc_hp는 현재까지 누적된 데미지라고 생각하면 된다.

 

damage_or_heal은 위에서 구한 것처럼 몬스터의 공격데미지와 힐을 구해서 만들어주면 된다.

 

여기서 중요한것은 acc_hp에 이 값을 더해주는 것이다.

 

이 값이 0보다 크게 되면 용사는 지금까지누적된 데미지를 넘어서 회복을 하게 된것 이므로 과다힐이 된것이다.

 

그래서 acc_hp 값이 0보다 크게 되면 0으로 초기화를 시켜준다.

 

그리고 acc_hp가 -이면 현재턴에서 용사가 딱 죽을 hp이다. 

 

즉 용사가 acc_hp의 절대값만큼의 hp를 가지고 있으면 이 해당턴에서 딱 0이 되어서 죽는것이다.

 

우리는 이 acc_hp의 절대값과 max_hp와 최대값을 계속 비교를 해서,

 

각 턴마다, 용사가 받는 최대 데미지를 구해준다.

 

이렇게 구한 max_hp를 가지고 용사가 던전에 들어가게 되면 중간에 피가 0이되어 죽으므로, 이 값보다 딱 1을 높게 해주면,

 

용사는 던전을 탐험하는 도중 받을수 있는 해당 턴의 누적데미지보다 hp가 크므로 죽지않고 던전을 돌파할 수 있게 된다.

 

이분 탐색이 아닌 방식으로도 풀 수 있는 것이었다.

 

 

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 1561 놀이공원  (0) 2021.09.02
[BOJ/백준] 1445 일요일 아침의 데이트  (0) 2021.09.02
[BOJ/백준] 11437 LCA  (0) 2021.07.31
[BOJ/백준] 3687 성냥개비  (0) 2021.07.31
[BOJ/백준] 2613 숫자구슬  (0) 2021.07.31
import sys
from collections import deque
input = sys.stdin.readline

def bfs(red,blue):
    stack = deque()

    stack.append((*red,*blue,0))
    dx = [-1,1,0,0]
    dy = [0,0,-1,1]
    while stack:
        rx,ry,bx,by,dis = stack.popleft()

        for i in range(4):
            nrx,nry = rx,ry
            r_p = 0
            while 0<=nrx<N and 0<=nry<M and arr[nrx][nry] != '#' and arr[nrx][nry] != 'O':
                nrx += dx[i]
                nry += dy[i]
                r_p += 1
            nbx,nby = bx,by
            b_p = 0
            while 0<=nbx<N and 0<=nby<M and arr[nbx][nby] != '#' and arr[nbx][nby] != 'O':
                nbx += dx[i]
                nby += dy[i]
                b_p += 1

            if (nbx,nby) == (nrx,nry):
                if arr[nbx][nby] == 'O':
                    continue
                if r_p > b_p:
                    nrx -= dx[i]
                    nry -= dy[i]
                else:
                    nbx -= dx[i]
                    nby -= dy[i]

            elif arr[nbx][nby] == 'O':
                continue
            elif arr[nrx][nry] == 'O':
                return dis+1
            nrx -= dx[i]
            nry -= dy[i]
            nbx -= dx[i]
            nby -= dy[i]
            if not visited[nrx][nry][nbx][nby]:continue
            visited[nrx][nry][nbx][nby] = False
            stack.append((nrx,nry,nbx,nby,dis+1))
    return -1

N,M = map(int,input().split())


arr = []
blue = []
red = []
for x in range(N):
    temp = list(input())
    for y in range(M):
        if temp[y] == 'B':
            blue = (x,y)
        elif temp[y] == 'R':
            red = (x,y)
    arr.append(temp)


visited = [[[[True for _ in range(M)] for _ in range(N)] for _ in range(M)] for _ in range(N)]


result = bfs(red,blue)
print(result)

 

 

구슬 탈출의 마지막 시리즈다.

 

구슬탈출3에서 썼던 코드에서 경로추적이랑 10이상일때 종료인것만 제외시켜줬다.

 

구슬탈출1~4까지는 다 똑같은 코드이므로,

 

하나만 잘 풀어놓으면

 

코드를 1~3군데만 고쳐도 전부 통과할수 있다.

+ Recent posts