DP 문제 테이블 방식은 2종류

 

dp[a][b] = a번의 횟수와 b의 높이가 남았을때의 최저 시도횟수

dp[a][b] = min(dp[a][b], 1 + max(dp[a-1][t-1], dp[a][b-t]))

1~b 사이에서 떨어드렸을 때, 깨지는 경우와 안깨지는 경우의 최악의 경우 max값을 구하고, 거기에 +1을 해준다.

횟수가 1회 남았을시에는 무조건 높이만큼의 횟수가 필요하니 그에 맞게 초기화

 횟수와 상관없이 1층만 남았을때에는 1회로 끝낼수 있으니 그걸로 초기화

 

 

 

다른 dp식 cozyyg 님의 코드를 보고 복기한거 틀릴 수 도 있음

 

dp[x][y] = x번 던질 기회가 남았을때 y번 시도해서 알 수 있는 유리가 깨지는 최소 높이

dp[x][y] = dp[x][y-1] + dp[x-1][y-1] + 1

y-1번을 시도했을때 x번 던질기회가 남았을때의 값과 ( 즉 깨지지 않았을때와) y-1번을 시도해서 x-1번 던질 기회가 남았을때(깨졌을때를 하면) 이 때의 최소높이는  이 두값을 더하고 + 1을 해준다.

 

두번째는 제대로 해석했는지는 모르겠다. 

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

[BOJ/백준] 3343 장미  (1) 2022.05.11
[BOJ/백준] 16890 창업  (0) 2022.05.11
[BOJ/백준] 19566 수열의 구간 평균  (0) 2022.01.15
[BOJ/백준] 12912 트리 수정  (0) 2021.12.23
[BOJ/백준] 2240 자두나무  (0) 2021.12.04
import sys
from math import ceil

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


N,AC,AP,BC,BP = map(int,input().split())

if AP*BC < BP*AC:
    AC,AP,BC,BP = BC,BP,AC,AP


answer = float('inf')

for A_COUNT in range(BC):
    B_COUNT = ceil((N-A_COUNT*AC)/BC)
    isover = False
    if B_COUNT<0:
        B_COUNT = 0
        isover = True
    answer = min(answer, A_COUNT*AP + B_COUNT*BP)
    if isover:
        break

print(answer)

 

 

헷갈려서 어려웠던 문제

 

최소공배수를 이용해서 풀어야하는 문제이다.

 

가성비가 좋은 것을 B로 시작하는것을 두고

 

가성비가 안 좋은 것을 A라고 햇을시,

 

AB개만큼 구매를 한다고 했을시에는 무조건 B를 A개 사는것이 이득이다.

 

즉 이말은 A를 B개 미만으로 사는것이 장미를 최소한의 값으로 구매하는것임을 인지하고,

 

A묶음의 장미를 B번 사는 것 미만으로 샀을때

 

장미의 값을 계산을 해주면 되는 문제이다.

 

 

문제를 풀때 a,b가 헷갈려서 너무 어려웠던 문제였다. 조심해서 풀자.

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

[BOJ/백준] 2695 공  (0) 2022.06.21
[BOJ/백준] 16890 창업  (0) 2022.05.11
[BOJ/백준] 19566 수열의 구간 평균  (0) 2022.01.15
[BOJ/백준] 12912 트리 수정  (0) 2021.12.23
[BOJ/백준] 2240 자두나무  (0) 2021.12.04
import sys
from collections import deque

def input():
    return sys.stdin.readline().rstrip()
A = list(input())

B = list(input())
N = len(A)

A.sort()
B.sort()
A = deque(A[:(N+1)//2])
B = deque(B[N-N//2:N])
answer = ['?']*N
left = 0
right = N-1

for idx in range(N):
    if idx%2:
        if A and A[0] >= B[-1]:
            answer[right] = B.popleft()
            right -= 1
        else:
            answer[left] = B.pop()
            left += 1
    else:
        if B and B[-1] <= A[0]:
            answer[right] = A.pop()
            right -= 1
        else:
            answer[left] = A.popleft()
            left += 1


print(''.join(answer))

 

문제를 보면, 구사과는 가장 사전순으로 빠르게,

 

큐브러버는 사전순으로 느리게이다.

 

이때 그냥 생각해보면 정렬하고, 

 

앞에서부터, 구사과는 작은걸

 

큐브러버는 큰걸 둔다고 생각을 하고 두면, 안된다.

 

이 문제는 최적의 방법이므로, 두사람의 패를 알고 있다고 가정을 하고 풀어야한다.

 

만약 구사과가 가진 문자열 중 가장 작은 것이, 큐브러버의 문자열중 가장 큰것보다, 작을때에는

 

작은걸 제일 앞에 두면 된다.

 

하지만 가장 작은것이 큐브러버 문자열 중 가장 큰것보다 크거나 같을때에는,

 

자신이 무조건 써야하는 패중에서 가장 큰걸 가장 뒤에 두는게 사전순으로 빠르게 하는방법이다.

 

이와 같이

 

큐브러버도 자신이 가진 문자열 중 가장 큰 것이, 구사과가 가진 가장 작은 문자열보다 클때에는 

 

앞에 자신의 가장 큰 문자열을 위치시키면 된다.

 

하지만 구사과가 가진 작은 문자열보다 자신이 가진 가장 큰 문자열이 작을때에는

 

자신이 가진 가장 작은 문자열을 뒤로 보내는것이, 사전순으로 늦게 하는방법일것이다.

 

이런식으로 해서 문제를 풀면된다.

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

[BOJ/백준] 2695 공  (0) 2022.06.21
[BOJ/백준] 3343 장미  (1) 2022.05.11
[BOJ/백준] 19566 수열의 구간 평균  (0) 2022.01.15
[BOJ/백준] 12912 트리 수정  (0) 2021.12.23
[BOJ/백준] 2240 자두나무  (0) 2021.12.04
import sys
from collections import defaultdict
def input():
    return sys.stdin.readline().rstrip()

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

arr = list(map(int,input().split()))


prefix_sum = [0] + arr[:]
result = 0
num_dict = defaultdict(int)
for i in range(1,N+1):
    prefix_sum[i] += prefix_sum[i-1]
    calc = prefix_sum[i] - M*i

    result += num_dict[calc]
    num_dict[calc] += 1


print(result+num_dict[0])

몇달 전에 푼 문제라 정확히 복기는 못하지만, 현재 생각 나는대로 복기를 할려고 한다.

 

 이 문제는 누적합을 이용해 구간의 평균합의 개수를 구하는 것이다.

 

 

현재까지의 누적합에서 지금까지의 평균의 총합을 빼주게 된다면, 

 

현재 위치에서 평균이 되기 위해 사라져야할 값이 보인다.

 

이 점을 이용해 푸는 문제이다.

 

구해야하는 평균이 K일때

 

즉 현재의 누적합 - 구간*K 을 했을때 나오는 값을 calc이라 했을 때,

 

이 calc 만큼만 사라지면 현재 구간 앞에서 얻을 수 있는 평균이 K인 구간의 개수가 되는 것이다.

 

이 문제는 누적합에서 구간의 평균합을 뺀 숫자와 일치하는 개수를 계속 누적해서 더해주면 풀 수 있는 문제이다.

 

그리고 마지막에 0을 더해준 이유는 현재 구간의 앞에서 나올 수 있는것을 더해준 것이니,

전체 구간에서 더해줄 필요가 있어서, 마지막에 더해주는 것이다.

 

6달전에 풀었던 것을 다시 복기할려니 정확하게 설명은 못했지만, 위와 같은 방법을 활용하면 된다.

 

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

[BOJ/백준] 3343 장미  (1) 2022.05.11
[BOJ/백준] 16890 창업  (0) 2022.05.11
[BOJ/백준] 12912 트리 수정  (0) 2021.12.23
[BOJ/백준] 2240 자두나무  (0) 2021.12.04
[BOJ/백준] 23291 어항정리  (0) 2021.11.07
import sys
sys.setrecursionlimit(3000)
def input():
    return sys.stdin.readline().rstrip()

def get_diameter():
    result = 0
    visited = [0 for _ in range(N)]
    def dfs(node,tag):
        nonlocal visited
        stack = [(node,0)]
        visited[node] += 1
        max_node = -1
        max_dis = 0
        while stack:
            node,dis = stack.pop()
            if dis>max_dis:
                max_dis = dis
                max_node = node
            for next_node in graph[node]:
                if visited[next_node] == tag:
                    visited[next_node] += 1
                    stack.append((next_node,dis + graph[node][next_node]))
        return [max_node,max_dis]
    for idx in range(N):
        if visited[idx] == 0:
            far_node,_ = dfs(idx,0)
            _,dis = dfs(far_node,1)
            result += dis

    return result
N = int(input())

graph = [{} for _ in range(N)]
origin_path = []
for _ in range(N-1):
    x,y,pay = map(int,input().split())
    if x>y:
        x,y = y,x
    graph[x][y] = pay
    graph[y][x] = pay
    origin_path.append((x,y,pay))

result = get_diameter()
cnt = 0
for x,y,pay in origin_path:
    del graph[x][y]
    del graph[y][x]

    result = max(result,get_diameter()+pay)
    graph[x][y] = pay
    graph[y][x] = pay
print(result)

이 문제를 푼 사람도 적어서 안 알려진 문제이지만, 좋은 문제라 생각되어 다른 것보다 우선순위를 두어 복기를 했다.

 

이 문제의 조건을 읽어보면 하나의 트리가 주어지고, 이 트리의 간선 중 하나를 제거 한뒤,

 

이 간선을 트리의 구조를 지키면서, 간선을 추가해야하는 문제이다.

 

처음에 생각한 방법은, 트리의 간선을 제거하고, 임의의 두 노드를 뽑아서, 연결이 되어있지않다면 연결을 해주고,

 

cycle이나, 모든 노드를 통과하지 못하는 경우를 체크해주고, 트리의 지름을 구하는 식으로 생각을 했다.

 

하지만 이 방법은 2000C2 * 1999 * 트리의 지름을 구하는 복잡도가 되어 시간 초과가 날거라 생각을 했다.

 

이 문제를 푼 아이디어는 다음과 같다.

 

우리는 처음에 하나의 트리가 주어진다.

 

이 트리의 간선을 하나를 제거한다는 것은

 

2개의 트리가 된다는 것이다.

 

이 와 같은 트리가 주어진다고 하면, 

 

 

하나의 간선을 없앤다는 것은 두개의 트리로 나뉘어진다는 것으로 볼수 있다.

 

우리는 이렇게 2개로 나뉘어진 트리를 이어주고, 트리의 지름을 최대로 구할려고 하는 것이다.

 

즉, 어느 노드 끼리 이어지는 것은 중요하지 않고, 2개의 트리를 연결했을때, 트리의 지름이 최대가 될려고 하는 것이다.

 

그러면 우리가 구하는 방법은 명확해진다 우리는 잘랐던 간선의 가중치를 그대로 연결한다.

 

그렇다하면, 2개의 트리의 어느 부분을 이었을때 가장 큰 트리의 지름이 될것인지 생각해보면,

 

명확하다. 2개의 트리에서 각각 지름을 구성하는 노드들의 끝점들을 이어주면 지름의 최대값이 될것이다.

 

즉 우리는 간선을 삭제하고, 나온 트리의 지름들을 각각 구한뒤, 자른 간선의 가중치를 더해주면

 

해당 간선을 삭제하고 연결했을때의 최대 트리의 지름을 구할 수 있게 된다.

 

 

그러면 이 방식은 간선의 개수 만큼 4번의 dfs를 하면 정답을 구할수 있게 된다.

 

여기서 나는, 2개로 나뉜 트리를 구분하기 위해, visited의 방문 회수를 기점으로, 언제 방문했는지를 구분해주었다.

 

 

이 문제는 처음에 푼 사람의 수가 워낙적고, 골1이라고 책정된 난이도 때문에 풀기 어려워보였지만,

 

문제에서 계속 강조하는 트리의 지름과 트리라는 구조라는 점에 착안해서 풀수 있었던 좋은 문제였다.

 

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

[BOJ/백준] 16890 창업  (0) 2022.05.11
[BOJ/백준] 19566 수열의 구간 평균  (0) 2022.01.15
[BOJ/백준] 2240 자두나무  (0) 2021.12.04
[BOJ/백준] 23291 어항정리  (0) 2021.11.07
[BOJ/백준] 23288 주사위 굴리기 2  (0) 2021.10.27
import sys

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


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


dp = [[0 for _ in range(W+1)] for _ in range(N)]

first = int(input())

if first == 1:
    dp[0][0] = 1
else:
    dp[0][1] = 1


for ind in range(1,N):
    num = int(input())
    num -= 1
    dp[ind][0] = dp[ind-1][0]
    if not num:
        dp[ind][0] += 1
    for j in range(1,W+1):
        dp[ind][j] = max(dp[ind-1][j],dp[ind-1][j-1])
        if j%2 == num:
            dp[ind][j] += 1

print(max(dp[N-1]))

다이나믹 프로그래밍을 이용한 문제였다.

 

 

2차원 배열 dp을 정의했다.

 

dp[N][W] 는 n번째일때의 w 번 이동했을때의 최대값인 경우로 dp 배열을 정의했다.

 

그러면 dp[n][w]일때의 값은 dp[n-1][w-1] 이나 dp[n-1][w]의 최대값이 될것이다. 이동을 했거나, 이동을 하지 않았거나 이다.

 

그리고 짝수번을 움직였을때에는 무조건 1번 나무에 있고, 홀수번을 움직였을때에는 무조건 2번나무에 위치해있는다.

 

이점을 이용해

 

dp[n][w] = max(dp[n-1][w-1],dp[n-1][w])을 비교해주고,

 

현재 떨어진 과일나무의 위치와 움직인 횟수의 홀수짝수가 동일 할 때에는

 

해당 위치에서 현재 떨어지는 열매를 먹을수 있는것이므로, 1개씩 더해주면 된다.

 

dp배열을 정의하고, 움직인 횟수가 홀수 짝수일때를 비교를 해, 1번나무에 위치해있는지,

 

2번나무에 위치해있는지를 구분해주면, 3차원 배열을 쓰지 않고도, 2차원 배열만으로도 풀수 있는 문제였따.

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

+ Recent posts