补题补到了个树上背包,然后发现孩子不会。这次就打算把一些该学的背包都学一手?大概。
- 我们知道,对于多重背包,有一个二进制拆分优化,可以在O(vlog(∑n[i]))级别的复杂度解决问题
- 然后单调队列优化可以跑到O(nv)
 思路
对于当前物品i,枚举选的个数n[i],用于更新dp数组
然后我们能够发现这样一个神必规律:
dp[i]用于维护代价为i的最大价值,且当前考虑选的物品代价为v
则dp[i]只能够从dp[j],当且仅当j mod v == i mod v 且选的个数不超过要求的点转移过来
也就是
dp[j+nv]=max(dp[j+(n−1)v+w,dp[j+(n−2)v+2w,....)
dp[j+nv]=max(dp[j],dp[j+v]−w,dp[j+2v]−2w,...)+nw
当然,因为有个数的限制,他选定的是一定区间里边的最大值,这个就给我们跑单调区间留下了伏笔捏
 CODE
| 12
 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
 33
 34
 35
 36
 37
 
 | #include<iostream>#include<stdio.h>
 #include<cstring>
 #include<vector>
 #include<math.h>
 #include<algorithm>
 #include <stdio.h>
 #include <queue>
 #include<bitset>
 #include<map>
 using namespace std;
 typedef long long ll;
 typedef unsigned long long ull;
 ll N, V;
 ll f[20000 + 5];
 ll g[20000 + 5];
 ll q[20000 + 5];
 int main() {
 cin >> N >> V;
 for (int i = 0; i < N; i++) {
 ll v, w, s;
 cin >> v >> w >> s;
 memcpy(g, f, sizeof(f));
 for (int j = 0; j < v; j++) {
 ll l = 0, r = -1;
 for (int k = j; k <= V; k += v) {
 while (r >= l && (k - q[l]) > s * v)l++;
 while (r >= l && (g[q[r]] - (q[r] - j) / v * w) < (g[k] - (k - j) / v * w))r--;
 q[++r] = k;
 f[k] = max(g[k], g[q[l]] + (k - q[l]) / v * w);
 }
 }
 }
 ll ans = 0;
 for (int i = 0; i <= V; i++)ans = max(f[i], ans);
 cout << ans;
 }
 
 |