본문 바로가기

알고리즘/백준

[백준] 2933번 : 미네랄

728x90

2933번: 미네랄

 

2933번: 미네랄

창영과 상근은 한 동굴을 놓고 소유권을 주장하고 있다. 두 사람은 막대기를 서로에게 던지는 방법을 이용해 누구의 소유인지를 결정하기로 했다. 싸움은 동굴에서 벌어진다. 동굴에는 미네랄

www.acmicpc.net

🤔 문제분석

미네랄이 떨어지는것을 구현하는게 이 문제의 핵심 입니다. 미네랄이 떨어지는 후보를 만들고, 그 후보가 땅에 떨어질때까지의 최소 거리를 구한뒤 그 최소거리로 미네랄 클러스터들을 이동시킨다.

최소거리를 구하는 방법은 너비우선탐색으로 구하였는데, 모든 후보들을 큐에 넣고 땅에 닫거나 다른 클러스터에 접촉되는경우 너비우선탐색을 종료하고 최소거리를 구한다. 해당 최소거리로 떨어지는 후보들을 움직이면 된다.

💻 코드

import sys
from collections import deque

input = sys.stdin.readline

R, C = map(int, input().split())
table = [list(str(input().strip())) for _ in range(R)]
N = int(input())
attack_list = list(map(int, input().split()))

LEFT = 0
RIGHT = 1

def change_direction(direction):
    if direction == LEFT:
        return RIGHT
    else:
        return LEFT

def attack(h, direction):
    if direction == LEFT:
        for i in range(C):
            if table[h][i] == 'x':
                table[h][i] = '.'
                return
    else:
        for i in range(C-1, -1 ,-1):
            if table[h][i] == 'x':
                table[h][i] = '.'
                return

def is_land(y, x):
    return y == R-1

def is_range(y, x):
    return R > y >=0 and C > x >=0

def find_min_height(queue):
    min_height = R
    for y, x in queue:
        cnt = 1
        for i in range(y+2, R):
            if (i, x) not in queue:
                if table[i][x] == 'x':
                    min_height = min(min_height, cnt)
                    break
            cnt += 1
            
        min_height = min(min_height, cnt)
    return min_height

def move(group):
    queue = deque()
    for q in group:
        queue.append((q[0], q[1], 0))
        table[q[0]][q[1]] = '.'
        
    while queue:
        y, x, cost = queue.popleft()
        
        if y == R-1 or table[y+1][x] == 'x':
            for n_y, n_x in group:
                table[n_y + cost][n_x] = 'x'
            break
                
        queue.append((y+1, x, cost + 1))

def move_down():
    # 바닥까지 혹은 크리스탈까지의 최소 거리를 찾아서 내린다.
    visited = [[False for _ in range(C)] for _ in range(R)]
    groups = []
    for i in range(R):
        for j in range(C):
            if table[i][j] == 'x' and not visited[i][j]:
                land_flag = False
                queue = deque()
                queue.append((i, j))
                group = []
                group.append((i, j))
                visited[i][j] = True
                
                while queue:
                    y, x = queue.popleft()
                    if is_land(y, x):
                        land_flag = True
                    
                    for dy, dx in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
                        ny, nx = dy + y, dx + x
                        if is_range(ny, nx) and not visited[ny][nx] and table[ny][nx] == 'x':
                            visited[ny][nx] = True
                            queue.append((ny, nx))
                            group.append((ny, nx))
                            
                if not land_flag:
                    groups.append(group)
                    
    for group in groups:
        move(group)
                    

direction = LEFT
for i in range(N):
    attack(R - attack_list[i], direction)
    # print("after attack", (attack_list[i], direction))
    # for t in table:
    #     print(''.join(t))
    
    
    move_down()
    
    # print("after move")
    # for t in table:
    #     print(''.join(t))
    
    direction = change_direction(direction)

for t in table:
    print(''.join(t))

🎯 피드백 및 개선사항

클러스터들을 얼마나 움직여야 하는지 생각해 보는것이 이 문제의 포인트 이었던것 같습니다. 처음에는 모든 후보들을 아래로 내려보면서 다 시도해보았지만 너비우선탐색으로 최소거리를 더 빠르게 알아 낼 수 있습니다. 복잡한 구현문제는 짧은 기능으로 쪼개고, 복잡한 로직은 천천히 더 많이 생각해 보는 시간을 갖도록 해야겠습니다 😀

728x90

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

[백준] 10800번 : 킬러볼  (0) 2024.02.04
[백준] 4574번 : 스노미노쿠  (0) 2024.02.04
[백준] 1113번 : 수영장 만들기  (1) 2024.01.31
[백준] 17143번 : 낚시왕  (1) 2024.01.30
[백준] 1509번 : 펠린드롬 분할  (2) 2024.01.28