https://www.acmicpc.net/problem/12865
제목을 보면 알 수 있듯이 그 유명한 배낭 문제이다.
먼저 입력을 살펴보면
N: 물건의 개수 (1 ~ 100) 10^2
W: 각 물건의 무게 (1 ~ 100,000) 10^5
V: 각 물건의 가치 (0 ~ 1,000) 10^3
K: 가방 속 최대무게 (1 ~ 100,000) 10^5
문제에서 원하는 출력은
배낭에 넣을 수 있는 물건들의 가치합의 최댓값이다.
시간 제한은 2초이므로 10^8까지는 연산해도 괜찮다.
<풀이>
DP 문제이므로 dp 배열을 만들고 각 값이 의미하는 바를 설정하는 게 중요하다.
이 문제는 배낭 문제이므로 2차원 배열을 사용한다.
dp[i][j]가 있을 때,
i: 임의로 설정한 각 물건의 번호로 1부터 N까지 반복한다.
j: 가방 속에 들어갈 수 있는 최대 무게로 1부터 K까지 반복한다.
dp[i][j]: i번째 물건까지 사용할 수 있을 때, j 무게까지의 가치합의 최댓값
위와 같이 설정하면 문제를 해결할 수 있다.
N은 10^2, K는 10^5까지 가능하므로 O(N * K) = 10^7으로 시간 범위 이내이다!
점화식
만약 j가 W[i] 이상이라면? i번째 물건을 가방 속에 집어넣을 수 있다는 뜻이다.
즉, i번째 물건을 넣지 않았을 때와 i번째 물건을 넣었을 때를 비교해야 한다.
넣었을 때에는 '배낭의 무게가 j-W[i]일때의 가치'와 배낭에 i번째 물건을 넣었으므로 V[i](i번째 물건의 가치)를 더해줘야 한다.
이걸 코드로 구현하면 다음과 같다.
if(j >= W[i]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-W[i]] + V[i]);
만약에 j가 W[i] 미만이라면? i번째 물건을 가방 속에 집어넣을 수 없다.
그렇다면 그냥 i-1번째 값에서 가져오면 된다.
이걸 코드로 구현하면 다음과 같다.
else dp[i][j] = dp[i-1][j];
예제의 입력을 통해 시뮬레이션을 돌려보자.
4 7 //n, k
6 13
4 8
3 6
5 12
먼저 첫 번째 물건은 무게가 6, 가치가 13이다.
이 경우 다음과 같이 채워진다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
1 | 0 | 0 | 0 | 0 | 0 | 13 | 13 |
2 | |||||||
3 | |||||||
4 |
두 번째 물건을 보자.
무게가 4, 가치가 8이다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
1 | 0 | 0 | 0 | 0 | 0 | 13 | 13 |
2 | 0 | 0 | 0 | 8 | 8 | 13 | 13 |
3 | |||||||
4 |
dp[2][6]에서 max를 통한 비교가 일어나 2번째 물건을 넣지 않았을 때 값이 더 크므로 위의 값을 가져온다.
세 번째 물건을 보자.
무게가 3, 가치가 6이다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
1 | 0 | 0 | 0 | 0 | 0 | 13 | 13 |
2 | 0 | 0 | 0 | 8 | 8 | 13 | 13 |
3 | 0 | 0 | 6 | 8 | 8 | 13 | 14 |
4 |
마지막 dp[3][7]이 핵심이다!
if(j >= W[i]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-W[i]] + V[i]);
위의 코드에 따라 max(dp[2][7], dp[2][4] + V[3])을 했을 때
max(13, 8 + 6) = max(13, 14) = 14
세 번째 물건을 넣었을 때 가치가 더 커지므로 넣은 값을 최댓값으로 한다!
마지막 물건을 보자.
무게가 5, 가치가 12이다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
1 | 0 | 0 | 0 | 0 | 0 | 13 | 13 |
2 | 0 | 0 | 0 | 8 | 8 | 13 | 13 |
3 | 0 | 0 | 6 | 8 | 8 | 13 | 14 |
4 | 0 | 0 | 6 | 8 | 12 | 13 | 14 |
결과적으로 dp[n][k]인 dp[4][7]을 출력해주면 가치합의 최댓값을 출력할 수 있다!
코드 전체를 보면 다음과 같다.
#include <iostream>
using namespace std;
int W[105], V[105];
int dp[105][100005];
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
int n, k;
cin>>n>>k;
for(int i = 1; i <= n; i++){
cin>>W[i]>>V[i];
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= k; j++){
if(j >= W[i]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-W[i]]+V[i]); //물건을 집어넣을 수 있을 때.
else dp[i][j] = dp[i-1][j];
}
}
cout<<dp[n][k];
return 0;
}
'알고리즘 별 문제 정리 > DP' 카테고리의 다른 글
[C++] 백준 2294: 동전 2 (0) | 2024.07.14 |
---|---|
[C++] 백준 1520: 내리막 길 (1) | 2024.07.13 |
[C++] 백준 11504: 가장 긴 바이토닉 부분 수열 (0) | 2024.07.10 |
[C++] 백준 2293: 동전 1 (0) | 2024.07.10 |
[C++] 백준 9251: LCS (0) | 2024.07.09 |