[백준 12920] 평범한 배낭 2 (C#, C++) - soo:bak
작성일 :
문제 링크
설명
같은 종류의 물건이 여러 개씩 있을 때, 배낭 용량을 초과하지 않으면서 만족도의 합을 최대로 하는 문제입니다.
접근법
같은 물건이 k개 있을 때 하나씩 k번 처리하면 너무 오래 걸립니다. 대신 1개, 2개, 4개, … 씩 묶어서 몇 개의 덩어리로 만들면 훨씬 빠릅니다.
예를 들어 물건이 13개 있으면 1개짜리, 2개짜리, 4개짜리, 6개짜리 네 덩어리로 나눕니다. 이 덩어리들을 적절히 골라 담으면 0개부터 13개까지 원하는 개수를 만들 수 있습니다. 13번 대신 4번만 처리하면 되는 셈입니다.
각 덩어리는 별개의 물건처럼 다룹니다. 덩어리의 무게와 만족도는 원래 값에 덩어리 개수를 곱한 것입니다.
이후 0/1 배낭 문제를 풉니다. 용량이 큰 쪽에서 작은 쪽으로 훑으면서 현재 덩어리를 넣었을 때와 넣지 않았을 때 중 더 나은 값을 고릅니다. 역순으로 훑어야 같은 덩어리를 중복해서 넣는 실수를 막을 수 있습니다.
모든 물건을 처리한 뒤 최댓값을 출력합니다.
Code
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
class Program {
static void Main() {
var first = Console.ReadLine()!.Split();
var n = int.Parse(first[0]);
var m = int.Parse(first[1]);
var dp = new int[m + 1];
for (var i = 0; i < n; i++) {
var parts = Console.ReadLine()!.Split();
var w = int.Parse(parts[0]);
var v = int.Parse(parts[1]);
var k = int.Parse(parts[2]);
var cnt = 1;
while (k > 0) {
var take = Math.Min(cnt, k);
var tw = w * take;
var tv = v * take;
for (var cap = m; cap >= tw; cap--)
dp[cap] = Math.Max(dp[cap], dp[cap - tw] + tv);
k -= take;
cnt <<= 1;
}
}
var ans = 0;
for (var i = 0; i <= m; i++) if (dp[i] > ans) ans = dp[i];
Console.WriteLine(ans);
}
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m; cin >> n >> m;
vi dp(m + 1, 0);
for (int i = 0; i < n; i++) {
int w, v, k; cin >> w >> v >> k;
int cnt = 1;
while (k > 0) {
int take = min(cnt, k);
int tw = w * take;
int tv = v * take;
for (int cap = m; cap >= tw; cap--)
dp[cap] = max(dp[cap], dp[cap - tw] + tv);
k -= take;
cnt <<= 1;
}
}
int ans = *max_element(dp.begin(), dp.end());
cout << ans << "\n";
return 0;
}