Description
有一个体积为
C
C
C 的背包和若干种物品.
前
n
n
n 种物品,第
i
i
i 种体积为
v
i
v_i
vi,价值
w
i
w_i
wi,有
d
i
d_i
di 件.
后
m
m
m 种物品,每种对应一个函数
f
(
x
)
=
a
i
x
2
+
b
i
x
+
c
i
f(x)=a_ix^2+b_ix+c_i
f(x)=aix2+bix+ci,表示若选
x
x
x 体积的这件物品,价值为
f
(
x
)
f(x)
f(x).(不选当作选
0
0
0 体积)
选择一些物品,使得体积和不超过
C
C
C,求最大价值.
Limitations
1
≤
n
≤
1
0
4
1\le n\le 10^4
1≤n≤104
1
≤
m
≤
5
1\le m\le 5
1≤m≤5
1
≤
C
≤
1
0
4
1\le C\le 10^4
1≤C≤104
1
≤
w
i
,
v
i
,
d
i
≤
1000
1\le w_i,v_i,d_i\le 1000
1≤wi,vi,di≤1000
∣
a
i
∣
,
∣
b
i
∣
,
∣
c
i
∣
≤
1000
|a_i|,|b_i|,|c_i|\le 1000
∣ai∣,∣bi∣,∣ci∣≤1000
1
s
,
128
MB
1\text{s},128\text{MB}
1s,128MB
Solution
不考虑后
m
m
m 件物品,那么就是一个多重背包,然而暴力跑 DP
铁定超时,需要优化.
多重背包一般有两种优化方式:二进制拆分和单调队列.
由于常数原因,我们选用二进制拆分,将每堆物品分成若干组,每组分别有
2
0
,
2
1
,
⋯
,
2
k
−
1
,
d
−
2
k
+
1
2^0,2^1,\cdots,2^{k-1},d-2^k+1
20,21,⋯,2k−1,d−2k+1 个(
k
k
k 是满足
n
−
2
k
+
1
>
0
n-2^k+1>0
n−2k+1>0 的最大整数).
那么用这些组就可以组出
0
∼
d
0\sim d
0∼d 个的所有选法,这样就转化成了
01
01
01 背包.
对于后 m m m 个物品,由于 m m m 很小,我们可以枚举给每个物品分配的体积,暴力更新.
总时间复杂度为 O ( C ∑ log d i + m C 2 ) O(C\sum\log d_i+mC^2) O(C∑logdi+mC2).
Code
1.15 KB , 1.83 s , 2.55 MB (in total, C++20 with O2) 1.15\text{KB},1.83\text{s},2.55\text{MB}\;\texttt{(in total, C++20 with O2)} 1.15KB,1.83s,2.55MB(in total, C++20 with O2)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, c;
scanf("%d %d %d", &n, &m, &c);
vector<pair<int, i64>> goods;
for (int i = 0, v, w, d; i < n; i++) {
scanf("%d %d %d", &v, &w, &d);
int t = 1;
while (d) {
if (d - t <= 0) t = d, d = 0;
else d -= t;
goods.emplace_back(t * v, 1LL * t * w);
t <<= 1;
}
}
vector<i64> dp(c + 1);
for (auto [v, w] : goods)
for (int k = c; k >= v; k--) chmax(dp[k], dp[k - v] + w);
for (int i = 0, A, B, C; i < m; i++) {
scanf("%d %d %d", &A, &B, &C);
auto f = [&](int x) { return 1LL * A * x * x + 1LL * B * x + C; };
for (int k = c; k >= 0; k--)
for (int j = 0; j <= k; j++) chmax(dp[k], dp[k - j] + f(j));
}
printf("%lld", dp[c]);
return 0;
}