1. 다중 배낭 문제(multiple 0-1 knapsack)
https://atcoder.jp/contests/past202206-open/tasks/past202206_h
H - Two Knapsacks
AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.
atcoder.jp
각각 무게 제한이 A,B인 2개의 가방이 있다고 하자.
이 2개의 가방에 무게가 W, 가치가 V인 물건을 담고자 한다.
2개의 가방에 담긴 물건 가치 합이 최대가 되도록 하는 방법은?
처음에는... 단순히 무게 제한이 A인 가방에 물건을 담고, 담은 물건은 제외한 다음 무게 제한이 B인 가방에 물건을 담는 식으로
for문을 2번 돌리는거지
n,a,b = map(int,input().split())
bag = []
for _ in range(n):
w,v = map(int,input().split())
bag.append((w,v))
value = 0
visited = [0]*(n)
for m in [a,b]:
dp = [[0,[]] for _ in range(m+1)]
for j in range(n):
if visited[j] == 0:
w,v = bag[j]
for i in range(1,m+1):
if i - w >= 0 and j not in dp[i-w][1]:
if dp[i-w][0] + v > dp[i][0]:
dp[i][1] = dp[i-w][1][:]
dp[i][1].append(j)
dp[i][0] = dp[i-w][0] + v
for j in dp[m][1]:
visited[j] = 1
value += dp[m][0]
print(value)
근데 이렇게 하면 틀리더라고
우연히 무게 제한이 A인 가방에 담긴 물건이 무게 제한이 B인 가방에 담길 때 가치가 최대일 수도 있으니까
무게 제한이 서로 같으면 맞을 것 같기는 한데 모르겠다...
--------------------------------------------------------------------------------------------------------------------------------------------------
결론은 모든 경우를 다 따져봐야 한다는 것
dp[i][j] = 첫번째 가방(무게 제한이 A)에 담긴 물건 무게 합이 i이고 두번째 가방(무게 제한이 B)에 담긴 물건 무게 합이 j일때 물건 가치의 합의 최댓값으로 정의
i = a,a-1,a-2,...,0으로 순회하고 j = b,b-1,b-2,..,0으로 순회하면서..
현재 보는 물건의 무게가 w, 가치가 v일때 i >= w, j >= w이면 두 가방에 담을 수 있으므로
dp[i][j] = max(dp[i-w][j] + v, dp[i][j-w] + v, dp[i][j])
i >= w이지만 j < w이면 첫번째 가방에만 담을 수 있으므로
dp[i][j] = max(dp[i-w][j] + v, dp[i][j])
i < w이지만 j >= w이면 두번째 가방에만 담을 수 있으므로
dp[i][j] = max(dp[i][j], dp[i][j-w] + v)
dp = [[0]*(b+1) for _ in range(a+1)]
for w,v in bag:
for i in range(a,-1,-1):
for j in range(b,-1,-1):
if i-w >= 0:
dp[i][j] = max(dp[i-w][j] + v, dp[i][j])
if j-w >= 0:
dp[i][j] = max(dp[i][j-w] + v, dp[i][j])
print(dp[a][b])
가방 1개일때와 똑같은 이유로 i = 0,1,2,..,a, j = 0,1,2,..,b 순방향으로 순회하면 물건이 중복해서 담길 수 있으므로 오답
dp[i][j][k]를 i번째 물건까지 봤을때, 첫번째 가방 무게 합이 j, 두번째 가방 무게 합이 k일때 물건 가치 합의 최대로 정의한다면
순방향으로 순회해서 해결할 수 있다
dp = [[[0]*(b+1) for _ in range(a+1)] for _ in range(n+1)]
for i in range(1,n+1):
w,v = bag[i-1]
for j in range(a+1):
for k in range(b+1):
if j-w >= 0 and k-w >= 0:
dp[i][j][k] = max(dp[i-1][j-w][k] + v, dp[i-1][j][k-w] + v, dp[i-1][j][k])
elif j-w >= 0:
dp[i][j][k] = max(dp[i-1][j-w][k] + v, dp[i-1][j][k])
elif k-w >= 0:
dp[i][j][k] = max(dp[i-1][j][k-w] + v, dp[i-1][j][k])
else:
dp[i][j][k] = dp[i-1][j][k]
print(dp[n][a][b])
가방이 3개이면 어떻게 할까?
dp[i][j][k]로 첫번째 가방의 무게 합이 i, 두번째 가방 무게 합이 j, 세번째 가방 무게 합이 k일 때 가치 합의 최댓값으로 하고
위와 똑같이 하면 되겠지
dp[i][j][k] = max(dp[i][j][k], dp[i-w][j][k] + v, dp[i][j-w][k] + v, dp[i][j][k-w] + v)
2. 비슷한 문제
가방 무게 제한이 w1, w2인 2개의 가방에 물건을 담을때 가치 합이 최대가 되도록 담는 문제로 위와 똑같은 문제다
#가방이 여러개인 배낭 문제
from sys import stdin
P = int(stdin.readline())
for p in range(1,P+1):
#첫번째 가방 무게 제한이 w1, 두번째 가방 무게 제한이 w2
n,w1,w2 = map(int,stdin.readline().split())
W = list(map(int,stdin.readline().split()))
V = list(map(int,stdin.readline().split()))
#dp[i][j] = 첫번째 가방에 담긴 물건 무게 합이 i, 두번째 가방에 담긴 물건 무게 합이 j
#일때 물건 무게 가치 합의 최댓값
dp = [[0]*(w2+1) for _ in range(w1+1)]
for i in range(n):
w = W[i]
v = V[i]
#역순으로 순회해야 물건이 중복해서 안담김
for j in range(w1,-1,-1):
for k in range(w2,-1,-1):
if j-w >= 0:
dp[j][k] = max(dp[j-w][k] + v, dp[j][k])
if k-w >= 0:
dp[j][k] = max(dp[j][k-w] + v, dp[j][k])
print(f'Problem {p}: {dp[w1][w2]}')
'알고리즘 > 다이나믹 프로그래밍' 카테고리의 다른 글
배열에서 일부를 골라 공차가 매우 큰 등차수열이 되도록 만드는 방법의 수 (0) | 2024.07.15 |
---|---|
다이나믹 프로그래밍을 이용한 partition minimum sum of difference of two subset(합의 차이가 최소가 되는 분할) 문제 해법 (0) | 2024.07.08 |
0-1 knapsack 배낭 문제 해법 반드시 알아야하는 디테일(물건을 중복해서 담는 경우) (0) | 2024.07.03 |
i < j < k < l을 O(N)에 도는 미친 다이나믹 프로그래밍 테크닉 (0) | 2024.06.26 |
다이나믹 프로그래밍으로 구하는 복수순열2 - 주어진 개수의 알파벳들만으로 만들 수 있는 문자열의 개수 (0) | 2024.06.20 |