(一)01背包
有个背包,可以装重为V的物品。有n种物品,第i个物品的重量(或说是空间)是v[i], 价值是c[i],每种物品只有一件,求能装的最大价值
二维解法:
定义二维数组f[i][j],表示前i件 重不超过j的物品的最大价值
动态转移方程:(根据第i件物品取或不取)
f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + c[i]);
文字的解释:
前i件重不超过j的物品的最大价值=取下面两项之最优
1 f[i-1][j]表示前i-1件重j的物品的最大价值,也就是 第i件没取,前i件的最大价值就是前i-1件的最大价值;
2 f[i-1][j-w[i]] + c[i] 表示前i-1件重j-w[i]的物品的最大价值 也就是第i件取了,占了w[i]的重量,增加了c[i]的价值
解是 f[n][V] (前n个物品重不超过V的最大价值)
#include <iostream>
using namespace std;
int n, V;
int v[200], c[200];
int f[1001][1001]; // f[i][j] --> 前i个物品,重量不超过j的最大价值
int main() {
cin >> V >> n;
for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
for(int i=1; i<=n; i++) { //循环前i件物品
for(int j=V; j>0; j--) { //循环重量j
if(v[i] <= j) f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + c[i]);
else f[i][j] = f[i-1][j]; //放不下
}
}
cout << f[n][V] << endl;
return 0;
}
一维解法:
定义数组f[j], 表示重不超过j的物品的最大价值
动态转移方程:(根据第i件物品取或不取)
f[j] = max(f[j], f[j-w[i]] + c[i]);
j循环必须是j=V...0
#include <iostream>
using namespace std;
int n, V;
int v[200], c[200];
int f[1001]; // f[j] --> 重量不超过j的最大价值
int main() {
cin >> V >> n;
for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
for(int i=1; i<=n; i++) { //循环前i件物品
for(int j=V; j>=v[i]; j--) { //循环重量j (j<v[i]时放不下,不操作)
f[j] = max(f[j], f[j-v[i]] + c[i]);
}
}
cout << f[V] << endl;
return 0;
}
(二)完全背包
有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],
每种物品有无限件,求能装的最大价值
一维解法:
定义数组f[j], 表示重不超过j的物品的最大价值
动态转移方程:同01背包一维解法一样(根据第i件物品取或不取)
f[j] = max(f[j], f[j-w[i]] + c[i]);
与01不同的是:
j循环必须是j=0...V
重不超过j的物品的最大价值
解是f[V](重量不超过V的最大价值)
#include <iostream>
using namespace std;
int n, V;
int v[200], c[200];
int f[1001]; // f[j] --> 重量不超过j的最大价值
int main() {
cin >> V >> n;
for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
for(int i=1; i<=n; i++) { //循环前i件物品
for(int j=v[i]; j<=V; j++) { //循环重量j (j<v[i]时放不下,不操作)
f[j] = max(f[j], f[j-v[i]] + c[i]);
}
}
cout << f[V] << endl;
return 0;
}
(三)多重背包
有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],
每种物品有s[i]件,求能装的最大价值
一维解法:
定义数组f[j], 表示重不超过j的物品的最大价值
动态转移方程:(根据第i件物品取或不取)
i=1...n;
j=V...0;
k=0...s[i] //K循环件数s[i]
f[j] = max(f[j], f[j-w[i]*k] + c[i]*k);
(多重背包与01背包一样:j循环是j=0...V)
解是f[V];
#include <iostream>
using namespace std;
int n, V;
int v[200], c[200], s[200];
int f[1001]; // f[j] --> 前需要占重量不超过j的最大价值
int main() {
cin >> V >> n;
for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];
for(int i=1; i<=n; i++) {
for(int j=V; j>=0; j--) {
for(int k=0; k<=s[i]; k++) {
if(j-v[i]*k < 0) break; //k是递增的,背包不够k循环结束
f[j] = max(f[j], f[j-v[i]*k] + c[i]*k);
}
}
}
cout << f[V] << endl;
return 0;
}
(四)混合背包
有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],
有的物品有1件,有的有s[i]件,有的有无限件,求能装的最大价值
解法:
i=1...n;
if(第i件是完全背包) ..
else if(第i件是多重背包) ..
//只能取1件作为s[i]为1的物品
可以根据混合情况变通if语句
三种背包-混合背包:
#include <iostream>
using namespace std;
int n, V;
int v[1001], s[1001], c[1001];
int f[1001];
int main() {
cin >> V >> n;
for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];
for(int i=1; i<=n; i++) {
if(s[i] == 0) {
for(int j=v[i]; j<=V; j++)
f[j] = max(f[j], f[j-v[i]]+c[i]);
} else {
for(int j=V; j>=v[i]; j--)
for(int k=1; k<=s[i]; k++) {
if(j >= k * v[i]) {
f[j] = max(f[j], f[j - k * v[i]] + k * c[i]);
} else break;
}
}
}
cout << f[V] << endl;
return 0;
}
(五)二维费用背包
二维费用只是直接在原有背包上加一维度和循环
1原来f[j]表示V不超过j的最大价值
现在f[j][k]表示V1不超过j且V2不超过k的最大价值
2 原来f[j] = max(f[j],f[j-v[i]] + c[i]);
现在f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);
这样连三维费用背包都会了..
这里直接是二维费用的混合三种背包:
#include <iostream>
using namespace std;
int n, V1, V2;
int v1[1001], v2[1001], m[1001], c[1001];
int f[1001][1001];
int main() {
cin >> n >> V1 >> V2;
for(int i=1; i<=n; i++) cin >> v1[i] >> v2[i] >> m[i] >> c[i];
for(int i=1; i<=n; i++) {
if(m[i] == 0) { //如果第i件是完全背包
for(int j=v1[i]; j<=V1; j++) {
for(int k=v2[i]; k<=V2; k++) {
f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);
}
}
} else { //如果第i件数量有限(01或多重)
for(int j=V1; j>=v1[i]; j--) {
for(int k=V2; k>=v2[i]; k--) {
for(int s=1; s<=m[i]; s++) {
if(j - s*v1[i] < 0 || k - s*v2[i] < 0) break;
f[j][k] = max(f[j][k], f[j-v1[i]*s][k-v2[i]*s] + c[i]*s);
}
}
}
}
}
cout << f[V1][V2] << endl;
return 0;
}
(六)有依赖性的背包
【金明的预算方案】
【解法】分五种情况处理主件:不取,只取主件,取主件和附件1,取主件和附件2,取主件和两个附件.找最优情况
#include <iostream>
using namespace std;
int V, n, num; //V是金钱,n是物品总数,num记录主件数
int v[61][4]; //v[i][0]记录有几个附件了,v[i][1]:主件的价格,v[i][2]:附件1,v[i][3]:附件2
int p[61][4]; //同上,记录重要度
int f[61][32001]; //f[i][j]表示前i件主件价格为j的最大价值
int map[61]; //i映射到v、p数组(num)
int main() {
int tmpv, tmpp, follow;
cin >> V >> n;
for(int i=1; i<=n; i++) {
cin >> tmpv >> tmpp >> follow;
if(follow == 0) {
v[++num][1] = tmpv;
p[num][1] = tmpp;
map[i] = num;
continue;
}
follow = map[follow];
v[follow][++v[follow][0]+1] = tmpv;
p[follow][++p[follow][0]+1] = tmpp;
}
for(int i=1; i<=num; i++) {
for(int j=1; j<=V; j++) {
f[i][j] = f[i-1][j];
if(j >= v[i][1]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]]+v[i][1]*p[i][1]);
if(j >= v[i][1]+v[i][2]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]);
if(j >= v[i][1]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][3]]+v[i][1]*p[i][1]+v[i][3]*p[i][3]);
if(j >= v[i][1]+v[i][2]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]-v[i][3]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]+v[i][3]*p[i][3]);
}
}
cout << f[num][V] << endl;
return 0;
}
(七)分组背包
N件物品,第i个物品的重量是v[i], 价值是c[i],每个物品有自己的组,每组只能选一个物品,求能装的最大价值
(可以看下原题 洛谷-分组背包)#include <iostream>
using namespace std;
int v[200][1010], c[200][1010];
int F[1010];
int V, N, G;
int main() {
int v1, c1, opt;
cin >> V >> N;
for(int i=1; i<=N; i++) {
cin >> v1 >> c1 >> opt;
G = max(G, opt);
v[opt][++v[opt][0]] = v1;
c[opt][++c[opt][0]] = c1;
}
for(int i=1; i<=G; i++)
for(int j=V; j>=0; j--)
for(int k=1; k<=v[G][0]; k++)
if(j >= v[i][k])
F[j] = max(F[j], F[j-v[i][k]] + c[i][k]);
cout << F[V] << endl;
return 0;
}
QWQ,就这样了...#end