개미 전사는 부족한 식량을 충당하고자 메뚜기 마을의 식량창고를 몰래 공격하려고 합니다. 메뚜기 마을에는 여러 개의 식량 창고가 있는데 식량 창고는 일직선으로 이어져 있습니다.
각 식량 창고에는 정해진 수의 식량을 저장하고 있으며 개미 전사는 식량 창고를 선택적으로 약탈하여 식량을 빼앗을 예정입니다. 이때 메뚜기 정찰병들은 일직선 상에 존재하는 식량 창고 중에서 서로 인접한 식량 창고가 공격 받으면 바로 알아챌 수 있습니다.
따라서 개미 전사가 정찰병에게 들키지 않고 식량 창고를 약탈하기 위해서는 최소한 한 칸 이상 떨어진 식량 창고를 약탈해야 합니다.
예를 들어 식량 창고 4개가 다음과 같이 존재한다고 가정합시다.
{1, 3, 1, 5}
이때 개미 전사는 두 번째 식량 창고와 네 번째 식량 창고를 선택했을 때 최댓값인 총 8개의 식량을 빼앗을 수 있습니다. 개미 전사는 식량 창고가 이렇게 일직선 상일 때 최대한 많은 식량을 얻기를 원합니다.
개미 전사를 위해 식량 창고 N개에 대한 정보가 주어졌을 때 얻을 수 있는 식량의 최댓값을 구하는 프로그램을 작성하세요.
풀이 시간 : 30분
시간 제한 : 1초
메모리 제한 : 128MB
입력 조건 :
- 첫째 줄에 식량 창고의 개수 N이 주어집니다. (3 <= N <= 100)
- 둘째 줄에 공백을 기준으로 각 식량 창고에 저장된 식량의 개수 K가 주어집니다. (0 <= K <= 1,000)
출력 조건 : 첫째 줄에 개미 전사가 얻을 수 있는 식량의 최댓값을 출력하세요.
입력 예시 :
4
1 3 1 5
출력 예시 :
8
N = 4일 때, 식량을 선택할 수 있는 경우의 수는 8가지
7번째 경우에서 8만큼의 식량을 얻을 수 있으므로 최적의 해는 8
ai = i번째 식량 창고까지의 최적의 해(얻을 수 있는 식량의 최댓값)
왼쪽부터 차례대로 식량창고를 턴다고 했을 때, 특정한 i번째 식량창고에 대해서 털지 안 털지의 여부를 결정하면, 아래 2가지 경우 중에서 더 많은 식량을 털 수 있는 경우를 선택하면 됩니다.
ai = i번째 식량 창고까지의 최적의 해(얻을 수 있는 식량의 최댓값)
ki = i번째 식량 창고에 있는 식량의 양
점화식은 다음과 같음
한 칸 이상 떨어진 식량 창고는 항상 털 수 있으므로 (i - 3)번째 이하는 고려할 필요가 없음
# 정수 N을 입력 받기
n = int(input())
# 모든 식량 정보 입력 받기
k = list(map(int, input().split()))
# 앞서 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [0] * 100
# 다이나믹 프로그래밍 진행 (보텀업)
d[0] = k[0]
d[1] = max(k[0], k[1])
for i in range(2, n):
d[i] = max(d[i - 1], d[i - 2] + k[i])
print(d[n - 1])
정수 X가 주어졌을 때, 정수 X에 사용할 수 있는 연산은 다음과 같이 4가지입니다.
정수 X가 주어졌을 때, 연산 4개를 적절히 사용해서 값을 1로 만들고자 합니다. 연산을 사용하는 횟수의 최솟값을 출력하세요. 예를 들어 정수가 26이면 다음과 같이 계산해서 3번의 연산이 최솟값입니다.
26 -> 25 -> 5 -> 1
풀이 시간 : 20분
시간 제한 : 1초
메모리 제한 : 128MB
입력 조건 : 첫째 줄에 정수 X가 주어집니다. (1 <= X <= 30,000)
출력 조건 : 첫째 줄에 연산을 하는 횟수의 최솟값을 출력합니다.
입력 예시
26
출력 예시
3
def solution(x):
dp = [0] * 30001
dp[2] = 1
dp[3] = 1
if x == 1:
return 0
elif x == 2 or x == 3:
return 1
num = 4
while num <= x:
n = num - 1
if num % 5 == 0:
if dp[n] > dp[int(num // 5)]:
n = int(num // 5)
if num % 3 == 0:
if dp[n] > dp[int(num // 3)]:
n = int(num // 3)
if num % 2 == 0:
if dp[n] > dp[int(num // 2)]:
n = int(num // 2)
dp[num] = dp[n] + 1
num += 1
return dp[x]
x = int(input())
print(solution(x))
def solution(x):
# 앞서 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [0] * 30001
# 다이나믹 프로그래밍 진행 (보텀업)
for i in range(2, x + 1):
# 현재의 수에서 1을 빼는 경우
d[i] = d[i - 1] + 1
# 현재의 수가 2로 나누어 떨어지는 경우
if i % 2 == 0:
d[i] = min(d[i], d[i // 2] + 1)
# 현재의 수가 3으로 나누어 떨어지는 경우
if i % 3 == 0:
d[i] = min(d[i], d[i // 3] + 1)
# 현재의 수가 5로 나누어 떨어지는 경우
if i % 5 == 0:
d[i] = min(d[i], d[i // 5] + 1)
return d[x]
x = int(input()) # 정수 X 입력 받기
print(solution(x))
풀이 시간 : 30분
시간 제한 : 1초
메모리 제한 : 128MB
입력 조건
-첫째 줄에 N, M이 주어진다. (1 <= N <= 100, 1 <= M <= 10,000)
- 이후의 N개의 줄에는 각 화폐의 가치가 주어진다. 화폐의 가치는 10,000보다 작거나 같은 자연수이다.
출력 조건
- 첫째 줄에 최소 화폐 개수를 출력한다.
- 불가능할 때는 -1을 출력한다.
입력 예시 1 :
2 15
2
3
출력 예시 1 :
5
입력 예시 2 :
3 4
3
5
7
출력 예시 2 :
-1
N = 3, M = 7이고, 각 화폐의 단위가 2, 3, 5인 경우
[Step 0] (초기화)
[Step 1]
[Step 2]
[Step 3]
# 정수 N, M을 입력 받기
n, m = map(int, input().split())
# N개의 화폐 단위 정보를 입력 받기
array = [int(input()) for _ in range(n)]
# 한 번 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [10001] * (m + 1)
# 다이나믹 프로그래밍 진행 (보텀업)
d[0] = 0
for i in range(n):
for j in range(array[i], m + 1):
if d[j - array[i]] != 10001: # (i - k)원을 만드는 방법이 존재하는 경우
d[j] = min(d[j], d[j - array[i]] + 1)
# 계산된 결과 출력
if d[m] == 10001: # 최종적으로 M원을 만드는 방법이 없는 경우
print(-1)
else:
print(d[m])
풀이 시간 : 30분
시간 제한 : 1초
메모리 제한 : 128MB
기출 : Flipkart 인터뷰
입력 조건
- 첫째 줄에 테스트 케이스 T가 입력됩니다. (1 <= T <= 1000)
- 매 테스트 케이스 첫째 줄에 n과 m이 공백으로 구분되어 입력됩니다. (1 <= n,m <= 20) 둘째 줄에 n x m 개의 위치에 매장된 금의 개수가 공백으로 구분되어 입력됩니다. (1 <= 각 위치에 매장된 금의 개수 <= 100)
출력 조건 : 테스트 케이스마다 채굴자가 얻을 수 있는 금의 최대 크기를 출력합니다. 각 테스트 케이스는 줄 바꿈을 이용해 구분합니다.
입력 예시
2
3 4
1 3 3 2 2 1 4 1 0 6 4 7
4 4
1 3 1 5 2 2 4 1 5 0 2 3 0 6 1 2
출력 예시
19
16
# 테스트 케이스 입력 받기
t = int(input())
for i in range(t):
# n, m 입력 받기
n, m = map(int, input().split())
# n x m 개의 위치에 매장된 금의 개수 입력 받기
maps = list(map(int, input().split()))
# DP 테이블 생성
dp = [0] * 401
for j in range(n):
# 첫 번째 열의 모든 행의 값은 기존 값으로 넣기
dp[j * m] = maps[j * m]
idx = 0
# 아래쪽 행의 변화를 위쪽 행에도 적용하기 위해 두 번 반복
for _ in range(2):
for idx in range(n * m - 1):
# 마지막 열 위치라면 이동할 수 없음
if (idx + 1) >= m and (idx + 1) % m == 0:
idx += 1
continue
else:
# 오른쪽으로 이동하는 경우
dp[idx + 1] = max(dp[idx + 1], dp[idx] + maps[idx + 1])
# 오른쪽 위로 이동하는 경우
if idx - m + 1 > 0:
dp[idx - m + 1] = max(dp[idx - m + 1], dp[idx] + maps[idx - m + 1])
# 오른쪽 아래로 이동하는 경우
if idx + m + 1 < n * m:
dp[idx + m + 1] = max(dp[idx + m + 1], dp[idx] + maps[idx + m + 1])
result = 0
# 마지막 열에서 가장 큰 값을 결과 값으로 선택
for k in range(n * m):
if (k + 1) >= m and (k + 1) % m == 0:
result = max(result, dp[k])
print(result)
금광의 모든 위치에 대하여 다음의 세 가지만 고려하면 됨
- 왼쪽 위에서 오는 경우
- 왼쪽 아래에서 오는 경우
- 왼쪽에서 오는 경우
세 가지 경우 중에서 가장 많은 금을 가지고 있는 경우를 테이블에 갱신해주어 문제를 해결함
array[i][j] = i행 j열에 존재하는 금의 양
dp[i][j] = i행 j열까지의 최적의 해 (얻을 수 있는 금의 최댓값)
점화식
이때 테이블에 접근할 때마다 리스트의 범위를 벗어나지 않는지 체크해야 함
편의상 초기 데이터를 담는 변수 array를 사용하지 않아도 됨
바로 DP 테이블에 초기 데이터를 담아서 다이나믹 프로그래밍을 적용할 수 있음
# 테스트 케이스 입력
for tc in range(int(input())):
# 금광 정보 입력
n, m = map(int, input().split())
array = list(map(int, input().split()))
# 다이나믹 프로그래밍을 위한 2차원 DP 테이블 초기화
dp = []
index = 0
for i in range(n):
dp.append(array[index:index + m])
index += m
# 다이나믹 프로그래밍 진행
for j in range(1, m):
for i in range(n):
# 왼쪽 위에서 오는 경우
if i == 0: left_up = 0
else: left_up = dp[i - 1][j - 1]
# 왼쪽 아래에서 오는 경우
if i == n - 1: left_down = 0
else: left_down = dp[i + 1][j - 1]
# 왼쪽에서 오는 경우
left = dp[i][j - 1]
dp[i][j] = dp[i][j] + max(left_up, left_down, left)
result = 0
for i in range(n):
result = max(result, dp[i][m - 1])
print(result)
N명의 병사가 무작위로 나열되어 있습니다. 각 병사는 특정한 값의 전투력을 보유하고 있습니다.
병사를 배치할 때는 전투력이 높은 병사가 앞쪽에 오도록 내림차순으로 배치를 하고자 합니다. 다시 말해 앞쪽에 있는 병사의 전투력이 항상 뒤쪽에 있는 병사보다 높아야 합니다.
또한 배치 과정에서는 특정한 위치에 있는 병사를 열외시키는 방법을 이용합니다. 그러면서도 남아 있는 병사의 수가 최대가 되도록 하고 싶습니다.
예를 들어, N = 7일 때 나열된 병사들의 전투력이 다음과 같다고 가정하겠습니다.
이때 3번 병사와 6번 병사를 열외시키면, 다음과 같이 남아 있는 병사의 수가 내림차순의 형태가 되며 5명이 됩니다. 이는 남아 있는 병사의 수가 최대가 되도록 하는 방법입니다.
병사에 대한 정보가 주어졌을 때, 남아 있는 병사의 수가 최대가 되도록 하기 위해서 열외시켜야 하는 병사의 수를 출력하는 프로그램을 작성하세요.
풀이 시간 : 40분
시간 제한 : 1초
메모리 제한 : 256MB
기출 : 핵심 유형
입력 조건 :
- 첫째 줄에 N이 주어집니다. (1 <= N <= 2,000)
- 둘째 줄에 각 병사의 전투력이 공백으로 구분되어 차례대로 주어집니다. 각 병사의 전투력은 10,000,000보다 작거나 같은 자연수입니다.
출력 조건 : 첫째 줄에 남아 있는 병사의 수가 최대가 되도록 하기 위해서 열외시켜야 하는 병사의 수를 출력합니다.
입력 예시
7
15 11 4 8 5 2 4
출력 예시
2
n = int(input())
array = list(map(int, input().split()))
count = 0
for i in range(1, n):
if array[i - 1] < array[i]:
count += 1
i += 1
print(count)
이 문제의 기본 아이디어는 가장 긴 증가하는 부분 수열(Longest Increasing Subsequence, LIS)로 알려진 전형적인 다이나믹 프로그래밍 문제의 아이디어와 같음
예를 들어 하나의 수열 array = {4, 2, 5, 8, 4, 11, 15}이 있다고 하면, 이 수열의 가장 긴 증가하는 부분 수열은 {4, 5, 8, 11, 15}가 됨
본 문제는 가장 긴 감소하는 부분 수열을 찾는 문제로 치환할 수 있으므로, LIS 알고리즘을 조금 수정하여 적용함으로써 정답을 도출할 수 있음
가장 긴 증가하는 부분 수열 (LIS) 알고리즘
n = int(input())
array = list(map(int, input().split()))
# 순서를 뒤집어 '최장 증가 부분 수열' 문제로 변환
array.reverse()
# 다이나믹 프로그래밍을 위한 1차원 DP 테이블 초기화
dp = [1] * (n)
# 가장 긴 증가하는 부분 수열(LIS) 알고리즘 수행
for i in range(1, n):
for j in range(0, i):
if array[j] < array[i]:
dp[i] = max(dp[i], dp[j] + 1)
# 열외해야 하는 병사의 최소 수를 출력
print(n - max(dp))