AtCoder ABC 060 D - Simple Knapsack (2Q, 水色, 400 点) (original) (raw)
面白い。各値に対する答えを予め整理して求めておく手法は頻出!
問題概要
個の品物がある。品物 は、重さが であり、価値が である。
いくつかの品物を、総和が 以下となるように選ぶとき、選んだ品物の価値の総和の最大値を求めよ。
制約
考えたこと
制約条件「」が怪しい。つまり、重さは 4 種類しかないということだ。ということは、重さごとに品物を整理したくなる。そして、同じ重さであれば、価値が高いものから品物を選びたくなる。このような考察から、次の値を求めることができるだろう。
values[i][j]
:重さが w[0] + i
であるような品物を j
個選んだときの、価値の最大値()
これを求めるのは簡単だ。単純に、重さが w[0] + i
である品物について、その価値を大きい順にソートして、その累積和を求めれば良いのだ。
全探索
上記の配列 values
が求まれば、次の全探索をすることで、答えが求められそうだ。
選んだとき、重さの総和が 以下である場合について、
values[0][i] + values[1][j] + values[2][k] + values[3][l]
の最大値を求めれば良いのだ。
最後に計算量を評価しよう。 のとりうる値の範囲は高々 以下なので、上記解法の計算量は となる。十分間に合う。
コード
#include <bits/stdc++.h> using namespace std; const long long INF = 1LL << 60;
int main() { long long N, W, min_w = INF; cin >> N >> W; vector w(N), v(N); for (int i = 0; i < N; i++) cin >> w[i] >> v[i];
vector<vector<long long>> items(4);
for (int i = 0; i < N; i++) items[w[i]-w[0]].push_back(v[i]);
for (int i = 0; i < 4; i++) sort(items[i].begin(), items[i].end(), greater<long long>());
vector<vector<long long>> values(4);
for (int i = 0; i < 4; i++) {
values[i].push_back(0);
for (int j = 0; j < items[i].size(); j++) values[i].push_back(values[i].back() + items[i][j]);
}
long long res = 0;
for (int i = 0; i < values[0].size(); i++) {
for (int j = 0; j < values[1].size(); j++) {
for (int k = 0; k < values[2].size(); k++) {
for (int l = 0; l < values[3].size(); l++) {
long long weight = w[0] * i + (w[0]+1) * j + (w[0]+2) * k + (w[0]+3) * l;
if (weight > W) continue;
res = max(res, values[0][i] + values[1][j] + values[2][k] + values[3][l]);
}
}
}
}
cout << res << endl;
}