Pack
背包专题
背包吼哇!
基础的背包分为OI01背包,完全背包,多重背包,二维背包,分组背包,树形背包,求方案数等..........
作为DP的一个基础部分还是有必要写一写的。
01背包:
一个物品能取1次。
设f[i][j]表示i物品j体积的最大权值,则状态转移方程:
f[i][j] = max(f[i - 1][j], f[i - 1][j - c[i]] + v[i]);
优化掉物品那一维:
for i 1...n
for j V...c[i]
f[j] = max(f[j], f[j - c[i]] + v[i]);
完全背包:
物品能取任意多次。
状态同上。
for i 1...n
for j c[i]...V
f[j] = max(f[j], f[j - c[i]] + v[i]);
多重背包:
一个物品可取若干次。
处理方法:
- 拆成01背包(过水)
- 二进制拆分(不会)
- 单调队列优化(上课讲了,没听懂...)
分组背包:
物品被分成若干组,每组只能选择至多1个。
状态:f[i][j]表示i组j体积的最大权值。
循环 + 状态转移方程:
for i 1...g
for j V...g[i].min_c
for k g[i].s...g[i].t
if(j >= c[k])
f[j] = max(f[j], f[j - c[k]] + v[k]);
二维背包:
费用限制为二维。
此时我们只需要把状态加一维即可解决。
for i 1...n
for jA VA...cA[i]
for jB VB...cB[i]
f[jA][jB] = max(f[jA][jB], f[jA - cA[i]][jB - cB[i]] + v[i]);
可行性完全背包:
可以用bitset解决
树形背包:
本质是分组背包。把子节点的不同体积看做组内不同物品。
例题:洛谷P2014 选课
1 #include <cstdio> 2 #include <algorithm> 3 4 const int N = 310; 5 6 struct Edge { 7 int nex, v; 8 }edge[N]; int top; 9 10 int e[N], v[N], f[N][N], n, m, root; 11 12 inline void add(int x, int y) { 13 top++; 14 edge[top].v = y; 15 edge[top].nex = e[x]; 16 e[x] = top; 17 return; 18 } 19 20 void DFS(int x) { 21 for(int i = e[x]; i; i = edge[i].nex) { 22 int y = edge[i].v; 23 DFS(y); 24 // cal 25 for(int j = m; j >= 1; j--) { // 0 1 pack 26 for(int k = 0; k <= j; k++) { 27 f[x][j] = std::max(f[x][j], f[x][j - k] + f[y][k]); 28 } 29 } 30 } 31 if(x != root) { 32 for(int i = m; i >= 1; i--) { 33 f[x][i] = f[x][i - 1] + v[x]; 34 } 35 } 36 return; 37 } 38 39 int main() { 40 scanf("%d%d", &n, &m); 41 root = n + 1; 42 for(int i = 1, x; i <= n; i++) { 43 scanf("%d%d", &x, &v[i]); 44 add(x ? x : root, i); 45 } 46 DFS(root); 47 printf("%d", f[root][m]); 48 return 0; 49 }
注意要倒序循环V的理由是这是01背包,一个物品只能取一次。