본문 바로가기
ALGORITHM/이코테

[ALGORITHM] 8. 다이나믹 프로그래밍

by 뭉망뭉 2022. 3. 6.

이것이 취업을 위한 코딩테스트다 with python 문제 풀이

8-5. 실전문제 '1로 만들기'

# C8. 다이나믹 프로그래밍 - 실전문제 '1로 만들기'

import sys
put = sys.stdin.readline

# 그리디로 elif문 써서 큰 수 순서대로 하면 안 되는 이유
# 맨 처음에 5로 나누는 것보다 1 빼고 시작하는 게 더 작은 케이스가 있을 수 있다!
# 그래서 부분문제로 나누고, 기존 연산값 재활용하는 dp로 해야 함

x = int(put())
d = [0]*30001 # DP 테이블 초기화

# x = 1일 때는 이미 1로 만들어졌으니까 연산 필요 없음 -> 2부터 시작
for i in range(2, x+1):
    # 뒤에 붙은 +1은 계산한 횟수 더하는 것!
    # -1, /2, /3, /5한 케이스 '모두' 계산한 후 그 중 최솟값을 고르고 +1 하면 됨

    # n이 7일 때 n에서 1을 빼는 연산 횟수 1번과, 6을 1로 만드는 d[6] 값을 더하면 d[7] 값 나옴. d[7] = d[6] + 1

    # 현재의 수에서 1을 빼는 경우
    d[i] = d[i-1] + 1

    # min(d[i] + 1, d[i // 5] + 1)가 아닌 이유는 
    # 1 빼는 케이스 하면서 d[i]에 +1 이미 해줘서.
    # min(d[i], d[i // 5] + 1)는
    # min(d[i-1] + 1, d[i // 5] + 1)와 동일. 
    # -1하는 케이스, 나누는 케이스 '모두' 한 후 그 중 최솟값 고르기! 
    if i % 5 == 0:
        d[i] = min(d[i], d[i // 5] + 1) 
    if i % 3 == 0:
        d[i] = min(d[i], d[i // 3] + 1)    
    if i % 2 == 0:
        d[i] = min(d[i], d[i // 2] + 1)

print(d[x])

 

8-6. 실전문제 '개미 전사'

# C8. 다이나믹 프로그래밍 - 실전문제 '개미 전사'

import sys
put = sys.stdin.readline

# i-1번째를 털면 현재(i번째)의 식량 창고는 못 함(연속돼서)
# i-2번째를 털면 현재(i번째)와 함께 털 수 있음
# a(i) = max(a(i-1), a(i-2) + k)

n = int(put())
foods = list(map(int, put().split()))

d = [0]*101 # dp 테이블 초기화

d[0] = foods[0] # 원소가 하나면 최대값은 자기 자신
d[1] = max(foods[0], foods[1]) # 두 개면 최대값은 둘 중 최대

for i in range(2, n):
    d[i] = max(d[i-1], d[i-2] + foods[i])

print(d[n-1]) # 리스트가 0부터 시작해서

 

8-7. 실전문제 '바닥 공사'

# C8. 다이나믹 프로그래밍 - 실전문제 '바닥 공사'

import sys
put = sys.stdin.readline

# 2×n 크기의 직사각형을 1×2, 2×1, 2x2 덮개로 채우는 방법의 수를 구하기 

n = int(put())
d = [0]*1001 # dp 테이블 초기화

# n = 1 -> 세로의 1개
# n = 2 -> 세로 / 가로1 + 2*2짜리 하나의 3개
# n = 3 -> 세로 / 가로2 + 2*2 2개의 5개

# i-1은 2*1의 하나
# i-2는 1*2 타일 2개나 2*2타일 하나로 하는 2가지 경우 (2*1로 하는 경우는 i-1에서 커버됨)

d[1] = 1
d[2] = 3
for i in range(3, n+1):
    d[i] = d[i-1] + d[i-2]*2

print(d[n] % 796796)

 

8-8. 실전문제 '효율적인 화폐 구성'

# C8. 다이나믹 프로그래밍 - 실전문제 '효율적인 화폐 구성'

import sys
put = sys.stdin.readline

# 화폐 종류 최소한으로 이용해 가치 합이 m원이 되도록
# 큰 단위 화폐가 작은 단위의 배수가 아니라 그리디 말고 dp로 풀어야

n, m = map(int, put().split())
worth = [int(put()) for _ in range(n)]

# 2, 3원으로 7원 만들기
# d[금액] = 화폐 개수
# d[5]는 d[3] + 1(2원)으로 만들 수 있음
# d[7]는 d[5] + 1(2원)으로 만들 수 있음
# d[7]는 d[7]이랑 d[i-k] + 1 중 최솟값

d = [10001]*(m+1) # dp 테이블 초기화

d[0] = 0 # 0원은 화폐 하나도 안 썼을 때 만들 수 있음
for money in worth:
    for i in range(money, m+1): # 화폐 단위보다 작은 값은 어차피 갱신될 필요도 없음
        # 이전(현재금액 - 화폐단위) 인덱스에 값이 존재하면 현재금액 해당 값 갱신 가능
        if d[i - money] != 10001:
            # 기존 d[i]랑 새로 갱신된 d[i - money] + 1 중 최솟값
            d[i] = min(d[i], d[i - money] + 1)

if d[m] == 10001: print(-1) # M원을 만드는 방법이 존재하지 않으면
else: print(d[m])

'ALGORITHM > 이코테' 카테고리의 다른 글

[ALGORITHM] 10. 그래프 이론  (0) 2022.03.08
[ALGORITHM] 9. 최단 경로  (0) 2022.03.07
[ALGORITHM] 7. 이진 탐색  (0) 2022.03.05
[ALGORITHM] 6. 정렬  (0) 2022.03.04
[ALGORITHM] 5. DFS/BFS  (0) 2022.03.01

댓글