DPL_1_B 0-1 ナップサック問題
# 目次
# 問題
https://onlinejudge.u-aizu.ac.jp/problems/DPL_1_B
それぞれ重さと価値をもった個のアイテムとという容量が与えられます.
容量を超えない範囲でうまくアイテムを選択した場合の価値の最大値を答える問題です.
# 入力
N W
v_1 w_1
v_2 w_2
...
v_N w_N
- N - アイテムの数
- W - 容量
- v_i w_i - 番目のアイテムの価値と重さ
# 出力
アイテムをうまく選んだ際の価値の最大値を出力してください.
# 入出力例
4 5
4 2
5 2
2 1
8 3
13
容量がなので、それを超えない範囲でアイテムとを選ぶと価値は最大値となります.
# 解説
各アイテムは選ぶかいなかの2択しかありません.
0-1ナップサック問題と呼ばれる典型問題です.
各アイテムを先頭から、選んだ場合と選ばなかった場合で価値が大きい方を選択していけば答えは出ます.
上の例の場合には以下のような木になります.
左に進むと選んだ場合、右に進むと選ばなかった場合の価値,重さ
です.
末端の葉の16通りの中で重さが5
以下で価値が最大のものが答えです.
0,0
/ \
4,2 0,0
/ \ / \
9,4 4,2 5,2 0,0
/ \ / \ / \ / \
11,5 9,4 6,3 4,2 7,3 5,2 2,1 0,0
/ \ / \ / \ / \ / \ / \ / \ / \
19,8 11,5 17,7 9,4 14,6 6,3 12,5 4,2 15,6 7,3 13,5 5,2 10,4 2,1 8,3 0,0
----
このようにアイテムを1つチェックするごとに場合分けが2通り発生しますので、計算量はとなります.
が20以下の場合には時間内に解けますが、今回はがと大きいので間に合いません.
参考用のためこの場合のソースコードは回答1にあります.
このような問題は動的計画法で効率よく解くことが出来ます.
まずは容量が0
と仮定すると、どのアイテムも選択出来ないので最大価値は当然0
となります.
容量が1
の時には、重さが1
以下のものを選択する場合はそのアイテムの価値がそのまま最大価値となります.
容量がcap+1
の時には、容量は多ければ多いほどアイテムを積められる可能性は高まるので価値は最低でも容量cap
の場合の最大値となります.
さらに、今回の容量で始めて解禁される組み合わせもあるはずなので今回のアイテムの重さ分を容量から引いた容量の時の最大価値+今回のアイテムの最大価値が最大値である可能性もあります.
これら2つの大きいほうがcap+1
の場合の最大価値になります.
最終的に容量W
、アイテムN
まで考慮した時の最大価値が答えになります.
動的計画法のテーブルは以下のようになります.
アイテムnまで考慮 \ 容量cap | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 5 | 5 | 9 | 9 |
3 | 0 | 2 | 5 | 7 | 9 | 11 |
4 | 0 | 2 | 5 | 8 | 10 | 13 |
# 計算量
# 回答1
TLE(時間制限超過)
struct Item {
Int w, v;
Item(): w(0),v(0) {}
Item(Int w, Int v): w(w), v(v) {}
};
istream & operator >> (istream & in, Item & item) {
Int v, w;
in >> v; in >> w; item.v = v; item.w = w;
return in;
}
#define MAX_N 100
#define MAX_W 10000
Int N, W;
vector<Item> items;
void input() {
cin >> N >> W;
Item a_;
while (cin >> a_) items.push_back(a_);
}
Int dfs(Int n, Int value, Int weight) {
if (n >= N || weight >= W) return value;
Int max_ = dfs(n + 1, value, weight);
if (weight + items[n].w <= W)
max_ = max(max_, dfs(n + 1, value + items[n].v, weight + items[n].w));
return max_;
}
void solve() {
cout << dfs(0, 0, 0) << endl;
}
int main() {
input();
solve();
return 0;
}
# 回答2(解答)
struct Item {
Int w, v;
Item(): w(0),v(0) {}
Item(Int w, Int v): w(w), v(v) {}
};
istream & operator >> (istream & in, Item & item) {
Int v, w;
in >> v; in >> w; item.v = v; item.w = w;
return in;
}
#define MAX_N 100
#define MAX_W 10000
Int N, W;
vector<Item> items;
vector<vector<Int> > DP(MAX_N + 1, vector<Int>(MAX_W + 1, 0));
void input() {
cin >> N >> W;
Item a_;
items.push_back(a_);
while (cin >> a_) items.push_back(a_);
}
Int dp() {
loop(n,1,N+1) {
Item item = items[n];
loop(cap,1,W+1) {
DP[n][cap] = DP[n-1][cap];
if (cap >= item.w)
DP[n][cap] = max(DP[n][cap], DP[n-1][cap-item.w] + item.v);
}
}
return DP[N][W];
}
void solve() {
cout << dp() << endl;
}
int main() {
input();
solve();
return 0;
}