文档链接:
http://love-oriented.com/pack/pack2alpha1.pdf
1. 01背包
1)题目:
有n件物品和一个容量为v的背包。
第i件物品的费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使价值总和最大
2)输入:
测试用例数
物品数 背包大小
n个物品的ci和wi
3)代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxV 1000
int main(void) {
int times, n, v, ci, wi;
int f[maxV];
freopen("1.txt", "r", stdin);
cin >> times;
while (times--) {
memset(f, 0, sizeof(f));
cin >> n >> v;
for (int i = 0; i < n; i++) {
cin >> ci >> wi;
for (int j = v; j >= 0; j--) {
if (j >= ci)
f[j] = max(f[j - ci] + wi, f[j]);
}
}
for (int i = 0; i <= v; i++) cout << f[i] << " ";
cout << endl;
cout << f[v] << endl;
}
}
4)测试文件:
2
4 10
2 4
3 5
4 6
5 10
5 20
3 2
7 3
10 5
15 6
16 10
2. 完全背包
1)题目:
有n件物品和一个容量为v的背包,每种物品都有无限件可用。
第i种物品的费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
2)输入:
测试用例数
物品数 背包容量
n个物品的ci和vi
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxV 1000
int main(void) {
int cases, n, v, ci, wi;
int f[maxV];
freopen("2.txt", "r", stdin);
cin >> cases;
while (cases--) {
memset(f, 0, sizeof(f));
cin >> n >> v;
for (int i = 0; i < n; i++) {
cin >> ci >> wi;
for (int j = 0; j <= v; j++) {
if (j >= ci)
f[j] = max(f[j], f[j - ci] + wi);
}
}
cout << f[v] << endl;
}
}
4)测试文件:
- 2
- 4 10
- 2 4
- 3 5
- 4 6
- 5 10
-
- 5 20
- 3 2
- 7 3
- 10 5
- 15 6
- 16 10
3. 多重背包
1)题目:
有n种物品和一个容量为v的背包。
第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
2)输入:
测试用例数
物品数 背包容量
n个物品的ni ci wi
3)代码:
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define maxV 1000
-
- int f[maxV], v;
-
- void ZeroOnePack(int ci, int wi) {
- for (int j = v; j >= 0; j--)
- if (j >= ci)
- f[j] = max(f[j], f[j - ci] + wi);
- }
-
- void CompletePack(int ci, int wi) {
- for (int j = 0; j <= v; j++)
- if (j >= ci)
- f[j] = max(f[j], f[j - ci] + wi);
- }
-
- void MultiplePack(int ni, int ci, int wi) {
- if (ni * ci >= v) {
- CompletePack(ci, wi);
- return;
- }
- int k = 1, amount = ni;
- while (k < ni) {
- ZeroOnePack(ci * k, wi * k);
- amount -= k;
- k *= 2;
- }
- ZeroOnePack(ci * amount, wi * amount);
- }
-
- int main(void) {
- int cases, n, ni, ci, wi;
- freopen("3.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- memset(f, 0, sizeof(f));
- cin >> n >> v;
- for (int i = 0; i < n; i++) {
- cin >> ni >> ci >> wi;
- MultiplePack(ni, ci, wi);
- }
-
- cout << f[v] << endl;
- }
- }
4)测试文件:
- 2
- 3 10
- 1 1 10
- 2 2 4
- 3 3 11
-
- 4 20
- 5 2 5
- 2 3 6
- 3 4 8
- 1 6 19
4. 混合三种背包
1)问题:
有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。
应该怎么求解呢?
2)输入:
测试用例数
物品数 背包容量
第i种物品的ni(无限次的标为-1) ci wi
3)代码:
- #include <iostream>
- #include <cstring>
- using namespace std;
- #define maxV 1000
-
- int f[maxV], v;
-
- void ZeroOnePack(int ci, int wi) {
- for (int j = v; j >= 0; j--)
- if (j >= ci)
- f[j] = max(f[j], f[j - ci] + wi);
- }
-
- void CompletePack(int ci, int wi) {
- for (int j = 0; j <= v; j++)
- if (j >= ci)
- f[j] = max(f[j], f[j - ci] + wi);
- }
-
- void MultiplePack(int ni, int ci, int wi) {
- if (ni * ci >= v) {
- CompletePack(ci, wi);
- return;
- }
- int k = 1, amount = ni;
- while (k < ni) {
- ZeroOnePack(ci * k, wi * k);
- amount -= k;
- k *= 2;
- }
- ZeroOnePack(ci * amount, wi * amount);
- }
-
- int main(void) {
- int cases, n, ni, ci, wi;
- freopen("4.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- memset(f, 0, sizeof(f));
- cin >> n >> v;
- for (int i = 0; i < n; i++) {
- cin >> ni >> ci >> wi;
- if (ni == 1) ZeroOnePack(ci, wi);
- else if (ni == -1) CompletePack(ci, wi);
- else MultiplePack(ni, ci, wi);
- }
- for (int i = 0; i <= v; i++) cout << f[i] << " "; cout << endl;
- cout << f[v] << endl;
- }
- }
4)测试文件:
- 2
- 3 10
- -1 1 2
- 2 2 5
- 1 3 7
-
- 4 20
- 3 2 5
- 4 4 6
- -1 6 8
- 1 7 10
5. 二维费用的背包
1)问题:
对于每件物品,具有两种不同的费用;
选择这件物品必须同时付出这两种代价;
对于每种代价都有一个可付出的最大值(背包容量)。
问怎样选择物品可以得到最大的价值。
第i件物品所需的两种代价分别为a[i]和b[i]。
两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]。
2)输入:
测试用例数
物品数 第一个背包容量v 第二个背包容量u
第i个物品的ai bi wi
3)代码:
- #include <iostream>
- #include <cstring>
- #include <algorithm>
-
- using namespace std;
- #define maxV 1000
- #define maxU 1000
-
- int main(void) {
- int cases, n, v, u, ai, bi, wi;
- int f[maxV][maxU];
- freopen("5.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- memset(f, 0, sizeof(f));
- cin >> n >> v >> u;
- for (int i = 0; i < n; i++) {
- cin >> ai >> bi >> wi;
- for (int j = v; j >= 0; j--) {
- for (int k = u; k >= 0; k--) {
- if (ai <= j && bi <= k)
- f[j][k] = max(f[j][k], f[j - ai][k - bi] + wi);
- }
- }
- }
- cout << f[v][u] << endl;
- }
- }
4)测试文件:
- 2
- 3 10 5
- 1 2 4
- 2 1 4
- 4 4 6
-
- 4 5 6
- 1 1 3
- 1 2 4
- 2 1 4
- 3 4 7
6. 分组背包
1)问题:
有n件物品和一个容量为v的背包。
第i件物品的费用是c[i],价值是w[i]。
这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
2)输入:
测试用例数
物品数 背包容量 组数g(组号范围:0 ~ g-1)
第i件物品的ci wi gi(所属组号)
3)代码:
- #include <iostream>
- #include <cstring>
- #include <vector>
- #include <algorithm>
-
- using namespace std;
- #define maxV 1000
- #define maxG 100
- #define maxN 100
-
- int main(void) {
- int cases, n, v, g, gi;
- int f[maxV];
- int ci[maxN], wi[maxN];
- freopen("6.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- memset(f, 0, sizeof(f));
- cin >> n >> v >> g;
- vector<vector<int> > gMap(g);
- for (int i = 0; i < n; i++) {
- cin >> ci[i] >> wi[i] >> gi;
- gMap[gi].push_back(i);
- }
-
- for (int i = 0; i < g; i++) {
- for (int j = v; j >= 0; j--) {
- for (int k = 0; k < gMap[i].size(); k++) {
- if (j >= ci[gMap[i][k]])
- f[j] = max(f[j], f[j - ci[gMap[i][k]]] + wi[gMap[i][k]]);
- }
- }
- }
-
- cout << f[v] << endl;
- }
- }
4)测试文件:
- 2
- 4 10 2
- 3 5 0
- 4 6 0
- 2 7 1
- 1 6 1
-
- 5 10 3
- 1 3 0
- 2 4 0
- 3 5 1
- 4 7 1
- 5 8 2
7. 有依赖的背包
1)题目:
这种背包问题的物品间存在某种“依赖”的关系。
也就是说,i依赖于j,表示若选物品i,则必须选物品j。
为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖;
另外,没有某件物品同时依赖多件物品。
2)输入:
测试用例数
物品数 背包大小
第i个物品的ci wi di(依赖物品的编号,-1为不依赖其他物品)
3)代码:
- #include <iostream>
- #include <vector>
- #include <cstring>
- #include <algorithm>
-
- using namespace std;
- #define maxV 1000
- #define maxG 100
-
- int main(void) {
- int cases, n, v, ci[maxV], wi[maxV], di, f[maxV];
- freopen("7.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- memset(f, 0, sizeof(f));
- cin >> n >> v;
-
-
-
-
- vector<vector<int> > groups(n);
-
-
- for (int i = 0; i < n; i++) {
- cin >> ci[i] >> wi[i] >> di;
- if (di == -1) groups[i].push_back(i);
- else groups[di].push_back(i);
- }
-
-
- for (int i = 0; i < n; i++) {
- if (groups[i].size() > 1) {
- vector<int> giOri;
- int newV = v - ci[i];
-
-
- for (int j = 0; j < groups[i].size(); j++) {
- if (groups[i][j] != i)
- giOri.push_back(groups[i][j]);
- }
-
-
- groups[i].clear();
- groups[i].resize(newV + 1, 0);
- for (int j = 0; j < giOri.size(); j++) {
- for (int k = newV; k >= 0; k--) {
- if (ci[giOri[j]] <= k) {
- groups[i][k] = max(groups[i][k], groups[i][k - ci[giOri[j]]] + wi[giOri[j]]);
- }
- }
- }
- }
- }
-
-
- for (int i = 0; i < n; i++) {
- if (groups[i].size() == 0) continue;
- else if (groups[i].size() == 1) {
- for (int j = v; j >=0; j--) {
- if (j >= ci[i])
- f[j] = max(f[j], f[j - ci[i]] + wi[i]);
- }
- } else {
- for (int j = v; j >= 0; j--) {
- for (int k = 0; k < groups[i].size(); k++) {
- if (j >= k + ci[i])
- f[j] = max(f[j], f[j - k - ci[i]] + groups[i][k] + wi[i]);
- }
- }
- }
- }
-
- cout << f[v] << endl;
- }
- }
4)测试文件:
- 2
- 3 10
- 3 5 -1
- 4 6 0
- 7 10 0
-
- 4 15
- 4 1 -1
- 6 8 0
- 7 10 0
- 10 4 -1
8. 泛化物品
1)题目:
在背包容量为v的背包问题中,泛化物品是一个定义域为0..v中的整数的函数h,当分配给它的费用为v时,能得到的价值就是h(v)。
为了应用泛化物品思想,这里假设题目为:存在依赖关系树(简单起见,只有一个根节点),即有依赖的物品也可以被依赖,可结合原文7.3节来理解此句。
2)输入:
测试用例数
物品数 背包容量 根节点编号
第i个物品的ci wi di(依赖物品的编号,-1为不依赖其他物品)
3)代码:
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define maxN 100
- #define maxV 1000
-
- int n, v;
- int cnt = 0;
- int head[maxN];
- int wi[maxN], ci[maxN];
- int f[maxN][maxV];
-
- struct Edge {
- int v, next;
- } e[maxN - 1];
-
- void addEdge(int u, int v) {
- e[cnt].v = v;
- e[cnt].next = head[u];
- head[u] = cnt++;
- }
-
- void treeDP(int u) {
- for (int i = ci[u]; i <= v; i++) {
- f[u][i] = wi[u];
- }
- for (int i = head[u]; i != -1; i = e[i].next) {
- int curr = e[i].v;
- treeDP(curr);
- for (int j = v; j >= 0; j--) {
- for (int k = j - ci[u]; k >= 0; k--) {
- f[u][j] = max(f[u][j], f[u][j - k] + f[curr][k]);
- }
- }
- }
- }
-
- int main(void) {
- int cases, root;
- freopen("8.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- cnt = 0;
- memset(head, -1, sizeof(head));
- memset(f, 0, sizeof(f));
- cin >> n >> v >> root;
- for (int i = 0; i < n; i++) {
- int di;
- cin >> ci[i] >> wi[i] >> di;
- addEdge(di, i);
- }
- treeDP(root);
- cout << f[root][v] << endl;
- }
- }
4)测试文件:
- 2
- 5 5 0
- 1 2 -1
- 2 7 0
- 2 5 0
- 2 6 2
- 1 4 2
-
- 6 5 0
- 1 2 -1
- 1 3 0
- 2 3 0
- 2 4 0
- 2 7 2
- 3 10 2
9. 背包问题的变化
1)题目:
输出01背包的具体方案
2)输入:
同01背包
3)代码:
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define maxV 1000
- #define maxN 100
-
- int main(void) {
- int cases, n, v, ci[maxN], wi;
- int f[maxV];
- bool g[maxN][maxV];
-
- freopen("9.txt", "r", stdin);
- cin >> cases;
- while (cases--) {
- memset(f, 0, sizeof(f));
- memset(g, 0, sizeof(g));
- cin >> n >> v;
- for (int i = 0; i < n; i++) {
- cin >> ci[i] >> wi;
- for (int j = v; j >= 0; j--) {
- if (j >= ci[i]) {
- if (f[j - ci[i]] + wi > f[j]) {
- f[j] = f[j - ci[i]] + wi;
- g[i][j] = 1;
- }
- }
- }
- }
-
- int i = n - 1, j = v;
- while (i >= 0) {
- if (g[i][j] == 1) {
- cout << "选了" << i << endl;
- j -= ci[i];
- } else {
- cout << "没选" << i << endl;
- }
- i--;
- }
- cout << endl;
- }
- }
4)测试文件:
- 2
- 4 10
- 2 4
- 3 5
- 4 6
- 5 10
-
- 5 20
- 3 2
- 7 3
- 10 5
- 15 6
- 16 10
参考:
参考1
参考2