- 문제 : https://www.acmicpc.net/problem/2636

 

문제 이해 

- 주어진 행렬에서 가장 바깥에 있는 치즈(값이 1인 칸)를 모두 한 번에 지워가면서 (1) 모두 사라지기 전 치즈 칸의 개수와 (2) 다 사라지기 까지의 시간을 구하는 문제다. 

 

- 일단 (1) 모두 사라지기 직전 치즈 칸의 개수를 출력하기 위해 초기 1의 개수(count)를 모두 구한 다음에 한 시간씩 치즈가 사라질 때마다 그 개수를 count에서 빼주면서 진행하면 된다. 모두 사라졌을 때는 count를 감소 시키는 단계가 없기 때문에 그대로 count를 출력하면 된다.

 

- (2) 시간은 bfs를 진행하는 횟수로 세면 된다. 

 

- 일단 바깥 치즈의 위치를 큐에 저장한 뒤, 탐색이 끝나면 그때 큐에 저장한 치즈를 모두 제거한다. (그때그때 치즈를 제거하면 바깥에 있지 않은 다른 치즈가 제거될 수 있기에 바깥 치즈를 모두 모았다가 한 번에 제거해야 한다)

 

- 대충 이렇게 까지는 그려졌는데, 가장 어려웠던 부분이 바깥 치즈의 위치를 구하는 것이었다. 1을 기준으로 근처에 0이 있는 걸로 하면, 내부에 구멍이 있는 경우도 해당되기 때문에 어떻게 해야할지 감이 안 잡혔다. 

 

- 그래서 풀이를 찾아보았더니, 값이 0인 (0, 0)에서 시작해 0을 기준으로 탐색을 하면 된다는 거다. 0을 기준으로 시작해 움직이다가 만나는 1은 바깥에 있는 값이다. 그리고 0을 만나면 이를 큐에 넣어서 탐색을 진행하면 된다. 바깥에 있는 1의 값은 melt에 넣고, 0은 queue에 넣어서 bfs를 진행하면 된다. 

 

- 그 외는 bfs 기본 로직이다. 

 

-> 가장 바깥에 있는 1을 구하는 방법 기억하자!!!!

 

 

정답 풀이 

from collections import deque

def bfs():
    queue = deque() # 값이 0인 좌표
    queue.append([0, 0])
    melt = deque()  # 가장 바깥에 있는 치즈 좌표
    while queue:
        x, y = queue.popleft()
        for i in range(4):
            nx, ny = x + dx[i], y + dy[i]
            if 0 <= nx < n and 0 <= ny < m and not visit[nx][ny] :
                visit[nx][ny] = 1
                if cheese[nx][ny] == 0:
                    queue.append([nx, ny])
                elif cheese[nx][ny] == 1:
                    melt.append([nx, ny])
    for x, y in melt:
        cheese[x][y] = 0
    return len(melt)
                
    
n, m = map(int, input().split())

cheese = []
count = 0 # 나중에 모두 없어지기 한 시간 전 치즈 칸 개수 세기 위함
time = 1
for i in range(n):
    data = list(map(int, input().split()))
    cheese.append(data)
    for j in range(m):
        if data[j] == 1:
            count += 1   

dx, dy = [-1, 1, 0, 0], [0, 0, -1, 1]
while True:
    visit = [[0] * m for _ in range(n)]
    melt_count = bfs()
    count -= melt_count
    if count == 0:
        print(time)
        print(melt_count)
        break
    time += 1

 

 

 

참고 블로그

Python-백준-2636-치즈-BFS

 

- 문제 : https://www.acmicpc.net/problem/15662

 

문제 이해 

- 전체 T개의 톱니바퀴가 주어지고, 각각의 톱니바퀴의 각 바퀴 칸에는 N극 또는 S극이 있다. 

 

 

- 문제에서 특정 톱니바퀴가 돌면 옆에 있는 톱니바퀴는 움직이는 톱니바퀴와 맞닿아 있는 부분이 어떤지에 따라 움직일 수도 있고, 아닐 수도 있다. 

    - 맞닿아 있는 부분이 S극 - N극, N극 - S극 인 경우는 맞닿아 있는 톱니바퀴도 움직인다.

    - 이때 주의해야할 건 기존 톱니바퀴 방향과는 반대로 회전한다는 것이다. 기존 톱니바퀴가 시계방향(direction = 1)으로 돈다면, 맞닿아 있는 톱니바퀴는 반시계 방향(direction = -1)로 돈다. 반대 방향도 마찬가지이다. 

    - 기준 톱니바퀴의 오른쪽으로 맞닿은 톱니바퀴의 정확한 칸 위치는 10시 방향(인덱스 6 또는 -2) 바퀴이고, 왼쪽으로 맞닿은 톱니바퀴는 2시 방향(인덱스 2)이다. 

 

 

- 위에서 주어진 조건대로 맞닿아 있는 바퀴의 위치도 정해져 있고, 극이 다를 경우 서로 반대로 회전하는 것까지 알 수 있다. 하지만 내가 막혔던 부분은 하나의 바퀴가 돌 때 오른쪽 혹은 왼쪽으로 계속 회전할텐데 그거를 queue로 해야하나 싶었는데 어떻게 구현해야하는지 몰랐다. 

    - 풀이를 찾아보니 문제에서 회전하는 톱니바퀴가 주어졌을 때, 그 톱니바퀴를 기준으로 오른쪽과 왼쪽 각각 for문으로 진행하면서 돌아가는 조건을 만족하는 톱니바퀴를 모두 배열에 넣은 다음에 한번에 모두 둘리면 된다. 만약 극이 같은 게 나오면 그즉시 종료된다. 

        # t 기준 오른쪽 기어
        for i in range(t+1, T+1):
            if gears[i][-2] != gears[i-1][2]: 
            	turnElement.append(i)
            else: 
            	break
                
        # t 기준 왼쪽 기어
        for i in range(t-1, 0, -1):
            if gears[i][2] != gears[i+1][-2]: 
            	turnElement.append(i)
            else: 
            	break

 

   

    - 톱니바퀴가 돌아가는 것은 각각의 인덱스가 오른쪽, 왼쪽으로 한칸씩 이동하는 것이므로 deque의 rotate를 이용하면 쉽다. 

    - 그리고 그때 기준이 되는 톱니바퀴(t)의 바로 양옆에 있는 것은 반대로 돌지만, 그게 아닌 한칸 떨어진 것들은 모두 톱니바퀴 t와 같은 방향으로 돈다. (이부분 생각하기!)

        # t 기어 회전
        gears[t].rotate(direction)
        
        # t 기어와 맞닿은 극이 다른 기어 회전
        for element in turnElement:
            if (element - t) % 2 != 0 :
                gears[element].rotate(-direction)
            else:
                gears[element].rotate(direction)

 

 

- 회전하는 톱니바퀴 인덱스와 회전 방향이 주어지는데, 이를 turn 배열에 넣은 뒤 반복문을 돌리면서 위 과정을 진행하면 된다.

 

 

- 모든 톱니바퀴가 돌았다면 마지막에는 각 톱니바퀴에서 0 인덱스 값이 1인 톱니바퀴의 개수를 구해서 출력하면된다. (더 편하게 하기 위해선 개수를 안 세도 되고, 값이 1이므로 톱니바퀴의 0인덱스 값을 모두 더한 값을 출력해도 된다)

    return sum([gears[i][0] for i in range(1, T+1)])

 

정답 풀이 

from collections import deque
import sys
input = sys.stdin.readline

def solution(T, gears, K, turn):
    for t, direction in turn:
        turnElement = []
        
        # t 기준 오른쪽 기어
        for i in range(t+1, T+1):
            if gears[i][-2] != gears[i-1][2]: 
            	turnElement.append(i)
            else: 
            	break
                
        # t 기준 왼쪽 기어
        for i in range(t-1, 0, -1):
            if gears[i][2] != gears[i+1][-2]: 
            	turnElement.append(i)
            else: 
            	break
                
        # t 기어 회전
        gears[t].rotate(direction)
        
        # t 기어와 맞닿은 극이 다른 기어 회전
        for element in turnElement:
            if (element - t) % 2 != 0 :
                gears[element].rotate(-direction)
            else:
                gears[element].rotate(direction)
            
    return sum([gears[i][0] for i in range(1, T+1)])

T = int(input())
gears = [0] + [deque(map(int,list(input().strip()))) for _ in range(T)]
K = int(input())
turn = [list(map(int, input().split())) for _ in range(K)]

res = solution(T, gears, K, turn)
print(res)

 

삽질한 코드,,,

wheels = []
t = int(input())
for _ in range(t):
    wheels.append(list(input()))

def move_straight(arr):
    temp = [arr[-1]]
    for i in range(len(arr) - 1):
        temp.append(arr[i])
    return temp 

def move_reverse(arr):
    temp = []
    for i in range(1, len(arr)):
        temp.append(arr[i])
    temp.append(arr[0])
    return temp
        
for _ in range(int(input())):
    number, direction = map(int, input().split())
    number -= 1
    
    if number == 0:
        if wheels[number][2] != wheels[number + 1][-2]:
            if direction == 1: # 시계방향이면 옆에는 반시계로
                wheels[number + 1] = move_reverse(wheels[number + 1])
            elif direction == -1:
                wheels[number + 1] = move_straight(wheels[number + 1])
    elif number == t - 1:
        if wheels[number][-2] != wheels[number - 1][2]:
            if direction == 1: # 시계방향이면 옆에는 반시계로
                wheels[number - 1] = move_reverse(wheels[number - 1])
            elif direction == -1:
                wheels[number - 1] = move_straight(wheels[number - 1])
    else:
        if wheels[number][2] != wheels[number + 1][-2]:
            if direction == 1:
                wheels[number + 1] = move_reverse(wheels[number + 1])
            elif direction == -1:
                wheels[number + 1] = move_straight(wheels[number + 1])
        if wheels[number][-2] != wheels[number - 1][2]:
            if direction == 1:
                wheels[number - 1] = move_reverse(wheels[number - 1])
            elif direction == -1:
                wheels[number - 1] = move_straight(wheels[number - 1])

answer = 0  
for wheel in wheels:
    if wheel[0] == '1':
        answer += 1
        
print(answer)

 

 

참고 블로그 

https://velog.io/@isayaksh/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-BOJ-15662-%ED%86%B1%EB%8B%88%EB%B0%94%ED%80%B4-2-Python

- 문제 : https://www.acmicpc.net/problem/15686

 

문제 이해 

- 처음에 보고 bfs로 풀어야 하나 싶었는데, 탐색이 아니고 문제를 잘 이해하고 브루트 포스로 구현하면 되는 문제다. 

- 문제를 이해한 바로는 다음과 같다. 

    - 주어진 행렬의 값 중 1인 값을 house로, 2인 값을 chicken 배열에 저장한다. 

    - 문제에서 현재 존재하는 치킨집(행렬 값 2) 중에 임의로 m개를 뽑는다고 했으므로 combinations으로 chicken 좌표 중에 임의의 m개를 뽑는다. 

    - 임의로 뽑은 m개의 치킨집 각각의 좌표에 대해 각 집과의 최소 거리의 합을 구한다. m개의 치킨집 후보가 여러개 있으므로 각각마다의 치킨거리(집 ~ 치킨집 최소거리) 합의 최솟값을 구하면 그게 정답이다. 

- 주의할 점 

    - 주의해야할 거는 행렬은 n x n 크기의 행렬이고, m은 앞으로 선택한 치킨집의 개수이다. 처음에 행렬 크기를 n x m 으로 했다가 틀렸다! (house와 chicken 배열에 값을 원래 개수와 다르게 추가할 가능성이 있기 때문)

 

 

정답 풀이1 

내가 푼 풀이

from itertools import combinations

n, m = map(int, input().split())

chicken = []
house = []
for i in range(n):
    temp = list(map(int, input().split()))
    for j in range(n):
        if temp[j] == 2:
            chicken.append([i, j])
        if temp[j] == 1:
            house.append([i, j])

answer = 1e9
candidates = list(combinations(chicken, m))
for candidate in candidates:
    count = 0
    for x, y in house:
        temp = 1e9
        for i, j in candidate:
            temp = min(temp, abs(x - i) + abs(y - j))
        count += temp
    answer = min(answer, count)
        
print(answer)

 

정답 풀이2

각 치킨 거리를 구하는 것만 함수로 빼는 풀이가 있었는데 시간이 차이가 꽤 많이 난다. 뭔가 함수로 빼고 말고가 시간복잡도에 영향을 주는게 있는걸까..??

 

def get_sum(candidate):
    count = 0
    for x, y in house:
        temp = 1e9
        for i, j in candidate:
            temp = min(temp, abs(x - i) + abs(y - j))
        count += temp 
    return count

치킨집의 위치를 임의로 m개를 구한 candidate와 house를 반복문 돌리면서 각각의 최소 거리 합을 구하는 로직이다.

 

from itertools import combinations

n, m = map(int, input().split())

chicken = []
house = []
for i in range(n):
    temp = list(map(int, input().split()))
    for j in range(n):
        if temp[j] == 2:
            chicken.append([i, j])
        if temp[j] == 1:
            house.append([i, j])

def get_sum(candidate):
    count = 0
    for x, y in house:
        temp = 1e9
        for i, j in candidate:
            temp = min(temp, abs(x - i) + abs(y - j))
        count += temp 
    return count 

answer = 1e9
candidates = list(combinations(chicken, m))
for candidate in candidates:
    answer = min(answer, get_sum(candidate))
        
print(answer)

- 문제 : https://www.acmicpc.net/problem/7490

 

문제 이해 

- 1부터 n까지의 수를 오름차순으로 정렬한 뒤 그 사이에 '+', '-', ' '(공백)의 연산을 넣은 뒤 연산의 결과가 0인 수식들을 출력하는 문제다. 

- 브루트포스 알고리즘으로 주어진 1 - n 숫자 사이에 계속 연산들을 넣고, 그 중 결과로 식별하면 된다고 이해까지는 했는데, 구체적으로 어떻게 구현해야할지 몰랐다. 

- 풀이를 찾아보니 일단 연산을 추가하는 거는 재귀함수로 돌리면 되고, 그렇게 구한 연산들을 숫자와 나란히 붙인뒤 값을 식별해 출력하면 된다.

    - 재귀함수 돌리고 목표 연산 개수인 k개에 도달하면, operator_list에 array를 deepcopy해서 추가해줘야 한다. (꼭 deepcopy를 해 야한다)

    - 연산 추가하는 순서도 중요하다 아스키 코드가 작은 ' ' (아스키 코드 34), '+' (아스키 코드 44), '-' (아스키 코드 45) 순서로 해야 틀리지 않는다. 

- 이때 만든 수식의 결과 값이 0인지 확인해야하는데, 문자열이므로 eval() 함수를 이용해 문자열을 계산해 결과가 0임을 확인하면된다. 여기에서 중요한 게 ' '(공백)은 count 되면 안되므로 ' '를 ''로 대체해서 eval을 진행하면 된다. 

 

근데 아직도 문제가 잘 이해 안 가는게 수식의 합이 0이어야 하는데, 아래 수식 같은 경우는 어떻게 결과가 0이 되는지 모르겠다,,,

1-2 3-4 5+6 7

그래서 pythontutor로 결과를 돌려봤는데  1-2(공백)3 인 경우에는 1-23으로 계산이 된다. 

 

 

정답 풀이 

import copy

def recursive(array, k):
    if len(array) == k:
        operator_list.append(copy.deepcopy(array))
        return
    
    array.append(' ')
    recursive(array, k)
    array.pop()
    
    array.append('+')
    recursive(array, k)
    array.pop()
    
    array.append('-')
    recursive(array, k)
    array.pop()
    
    
t = int(input())
for _ in range(t):
    n = int(input())
    operator_list = []
    recursive([], n - 1)
    integer = [i for i in range(1, n + 1)]
    
    for operator in operator_list:
        string = ''
        for i in range(n - 1):
            string += str(integer[i]) + operator[i]     
        string += str(integer[-1])
        if eval(string.replace(' ', '')) == 0:
            print(string)
    print()

 

참고 블로그 : https://reliablecho-programming.tistory.com/104

+ Recent posts