- 개미 전사는 부족한 식량을 충당하고자 메뚜기 마을의 식량창고를 몰래 공격하려고 한다. 메뚜기 마을에는 여러 개의 식량창고가 있는데 식량창고는 일직선으로 이어져 있다.
- 각 식량창고에는 정해진 수의 식량을 저장하고 있으며 개미 전사는 선택적으로 약탈하여 식량을 빼앗을 예정이다. 이때 메뚜기 정찰병들은 일직선상에 존재하는 식량창고 중에서 서로 인접한 식량창고가 공격받으면 바로 알아챌 수 있다.
- 따라서 개미 전사가 정찰병에게 들키지 않고 식량창고를 약탈하기 위해서는 최소한 한 칸 이상 떨어진 식량창고를 약탈해야 한다.
예시
1 3 1 5
- 개미 전사는 두 번째 식량창고와 네 번째 식량창고를 선택했을 때 최댓값인 총 8개의 식량을 빼앗을 수 있다. 개미 전사는 식량창고가 이렇게 일직선상일 때 최대한 많은 식량을 얻기를 윈한다.
- 개미전사를 위해 식량창고 N개에 대한 정보가 주어졌을 때 얻을 수 있는 식량의 최댓값을 구하는 프로그램을 작성하시오.
특정 식량 창고에 대해서 털지 안 털지의 여부를 결정하면, 아래의 2가지 경우 중 더 많은 식량을 털 수 있는 경우를 선택하면 됨
번째 식량 창고를 털려면 번째 식량 창고가 털려있으면 안 되고, 번째 식량 창고는 털려 있어도 됨
따라서 번째 식량 창고까지의 최적의 해는 다음 두 가지 경우 중 큰 값으로 선택 가능
점화식
N = int(input())
arr = list(map(int, input().split()))
dp = [0] * 100
# 다이나믹 프로그래밍 진행(보텀업)
dp[0] = arr[0]
dp[1] = max(arr[0], arr[1])
for i in range(2, N):
dp[i] = max(dp[i-1], dp[i-2]+arr[i])
print(dp[N-1])
dp[1] = arr[1]
로 초기화 함dp[1] = max(arr[0], arr[1])
해줘야 원소 두 개일 때도 최댓값이 나옴
- 정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.
- X가 3으로 나누어 떨어지면, 3으로 나눈다.
- X가 2로 나누어 떨어지면, 2로 나눈다.
- 1을 뺀다.
- 정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.
점화식
: 에서의 최적의 해(최소 연산 횟수)
는 그 이전 단계의 최적의 해 중 최솟값에 1을 더한 값이 될 수 있음
이전 단계란, 에서 주어진 연산을 수행해 만들 수 있는 모든 경우를 의미함
예시
= 6일 때 주어진 연산 중 수행 가능한 경우는
각 경우의 최적의 해
따라서 점화식은 다음과 같음
N = int(input())
dp = [0] * 30001
for i in range(2, N+1):
# 1을 빼는 건 무조건 가능 -> 직전 연산 횟수 + 1를 default로 설정
dp[i] = dp[i-1] + 1
# 3으로 나누어 떨어질 때 dp 값 갱신 가능
if i%3 == 0:
dp[i] = min(dp[i], dp[i//3]+1)
# 2로 나누어 떨어질 때 dp 값 갱신 가능
if i%2 == 0:
dp[i] = min(dp[i], dp[i//2]+1)
print(dp[N])
- N가지 종류의 화폐가 있다.
- 이 화폐들의 개수를 최소한으로 이용해서 그 가치의 합이 M원이 되도록 하려고 한다.
- 이때 각 화폐는 몇 개라도 사용할 수 있으며, 사용한 화폐의 구성은 같지만 순서만 다른 것은 같은 경우로 구분한다.
- 예를 들어 2원, 3원 단위의 화폐가 있을 때는 15원을 만들기 위해 3원을 5개 사용하는 것이 가장 최소한의 화폐 개수이다.
- M원을 만들기 위한 최소한의 화폐 개수를 출력하는 프로그램을 작성하시오.
점화식
각 화폐 단위인 k를 하나씩 확인하며
- 를 만드는 방법이 존재하는 경우:
- 를 만드는 방법이 존재하지 않는 경우:
예시
N = 3, M = 7, 화폐의 단위 2, 3, 5
Step 0
Step 1
Step 2
Step 3
N, M = map(int, input().split())
arr = [int(input()) for _ in range(N)]
dp = [10001] * (M+1) # inf로 초기화
dp[0] = 0
for i in range(N): # N개의 화폐 단위에 대해서
k = arr[i]
for j in range(k, M+1): # i보다 큰 M 이하 자연수에 대해서
if dp[j - k] != 10001: # dp[j-k] 존재하면
dp[i] = min(dp[j], dp[j-k]+1) # 리스트 갱신 가능
if dp[-1] == 10001:
print(-1)
else:
print(dp[-1])
k = arr[i]
한 거, 생략 가능
- n x m 크기의 금광이 있다. 금광은 1 x 1 크기의 칸으로 나누어져 있으며, 각 칸은 특정한 크기의 금이 들어 있다.
- 채굴자는 첫 번째 열부터 출발하여 금을 캐기 시작한다. 맨 처음에는 첫 번째 열의 어느 행에서든 출발할 수 있다.
- 이후에 m - 1번에 걸쳐서 매번 오른쪽 위, 오른쪽, 오른쪽 아래 3가지 중 하나의 위치로 이동해야 한다.
- 결과적으로 채굴자가 얻을 수 있는 금의 최대 크기를 출력하는 프로그램을 작성하시오.
점화식
T = int(input())
for case in range(T):
N, M = map(int, input().split())
arr = list(map(int, input().split()))
dp = [] # dp 테이블 초기화
idx = 0
for i in range(N):
dp.append(arr[idx : idx+M])
idx += M
# 다이나믹 프로그래밍
for j in range(1, M): # 항상 왼쪽에서 오니까 열부터
for i in range(N):
# left_up 정의
if i == 0: # 왼쪽 위에서 오는 게 불가능하면
left_up = 0
else:
left_up = dp[i-1][j-1]
# left_down 정의
if i == N-1:
left_down = 0
else:
left_down = dp[i+1][j-1]
left = dp[i][j-1]
dp[i][j] += max(left, left_up, left_down)
result = 0
for i in range(N):
result = max(result, dp[i][M-1])
print(result)
- N명의 병사가 무작위로 나열되어 있으며, 각 병사는 특정한 값의 전투력을 보유하고 있다.
- 병사를 배치할 때는 전투력이 높은 병사가 앞쪽에 오도록 내림차순으로 배치를 하고자 한다. 다시 말해 앞쪽에 있는 병사의 전투력이 항상 뒤쪽에 있는 병사보다 높아야 한다.
- 또한 배치 과정에서는 특정한 위치에 있는 병사를 열외시키는 방법을 이용한다.
- 그러면서도 남아있는 병사의 수가 최대가 되도록 하고 싶다.
예시
- 병사에 대한 정보가 주어졌을 때, 남아있는 병사의 수가 최대가 되도록 하기 위해서 열외해야 하는 병사의 수를 출력하는 프로그램을 작성하시오.
점화식
모든 에 대하여,
이 문제는 가장 긴 감소하는 부분 수열을 찾는 문제로 치환 가능 → LIS 알고리즘 조금 수정해 적용 가능
입력 받은 병사 정보를 뒤집어 LIS 알고리즘 수행
N = int(input())
arr = list(map(int, input().split()))
# 순서 뒤집어서 '최장 증가 부분 수열' 문제로 변환
arr.reverse()
dp = [1] * N
for i in range(1, N):
for j in range(0, i):
if arr[j] < arr[i]:
dp[i] = max(dp[i], dp[j]+1)
# 열외해야 하는 병사의 최소 수를 출력
print(N - max(dp))
Source
이코테 2021 다이나믹 프로그래밍