背包dp
背包dp的模型可以看作是“给定n个物品,从中选择一定的物品”。
我们从以下几个角度考虑:
1.已经选择的物品
2.背包容量
dp数组常常保存最大价值
0/1背包(一个物品只选一次)
#include <bits/stdc++.h> using namespace std; const int maxn = 1010; int f[maxn] = {}; //默认全为0,这样后面就不需要再初始化 int n = 0, m = 0; //n件物品,m为背包总容量 int v[maxn] = {}, w[maxn] = {}; //v表示第i件物品体积,w为第i件物品价值 int main() { scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) scanf("%d%d", &v[i], &w[i]); for(int i=1; i<=n; i++) { for(int j=m; j>=v[i]; j--) { f[j] = max(f[j], f[j-v[i]] + w[i]); } } printf("%d", f[m]); return 0; }
完全背包(物品数量无限制)
#include <bits/stdc++.h> using namespace std; const int maxn = 1010; int f[maxn] = {}; //默认全为0,这样后面就不需要再初始化 int n = 0, m = 0; //n件物品,m为背包总容量 int v[maxn] = {}, w[maxn] = {}; //v表示第i件物品体积,w为第i件物品价值 int main() { scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) scanf("%d%d", &v[i], &w[i]); for(int i=1; i<=n; i++) { for(int j=v[i]; j<=m; j++) { f[j] = max(f[j], f[j-v[i]] + w[i]); } } printf("%d", f[m]); return 0; }
多重背包(给定物品数量)
直接转化为0/1背包
#include <bits/stdc++.h> using namespace std; const int maxn = 110; int n = 0, m = 0; int f[maxn] = {}; int v[maxn] = {}, w[maxn] = {}, s[maxn] = {}; int main() { scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) { scanf("%d%d%d", &v[i], &w[i], &s[i]); } for(int i=1; i<=n; i++) { for(int j=1; j<=s[i]; j++) { for(int k=m; k>=v[i]; k--) { f[k] = max(f[k], f[k-v[i]] + w[i]); } } } printf("%d", f[m]); return 0; }
多重背包 & 二进制拆分
#include <bits/stdc++.h> using namespace std; const int maxn = 15000; const int maxm = 2010; int n = 0, m = 0; int f[maxm] = {}; int v[maxn] = {}, w[maxn] = {}, s[maxn] = {}, cnt = 0; int main() { int vi = 0, wi = 0, si = 0; scanf("%d%d", &n, &m); //二进制拆分 for(int i=1; i<=n; i++) { scanf("%d%d%d", &vi, &wi, &si); if(si > m / vi) si = m / vi; for(int j=1; j<=si; j<<=1) { v[++cnt] = j * vi; w[cnt] = j * wi; si -= j; } if(si > 0) { v[++cnt] = si * vi; w[cnt] = si * wi; } } //0/1背包 for(int i=1; i<=cnt; i++) { for(int j=m; j>=v[i]; j--) { f[j] = max(f[j], f[j-v[i]] + w[i]); } } printf("%d", f[m]); return 0; }
分组背包
分组背包 & 终极版1
#include <bits/stdc++.h> using namespace std; const int maxn = 40; const int maxm = 210; //分组背包 int n = 0, m = 0, t = 0; int v[maxn] = {}, c[maxn] = {}, g[15][maxn] = {}; int f[maxm] = {}; int main() { int x = 0; scanf("%d%d%d", &m, &n, &t); for(int i=1; i<=n; i++) { scanf("%d%d%d", &v[i], &c[i], &x); g[x][++g[x][0]] = i; } for(int i=1; i<=t; i++) { for(int j=m; j>=0; j--) { for(int k=1; k<=g[i][0]; k++) { if(j >= v[g[i][k]]) { x = g[i][k]; f[j] = max(f[j], f[j-v[x]] + c[x]); } } } } printf("%d", f[m]); return 0; }
分组背包 & 终极版2
#include <bits/stdc++.h> using namespace std; const int maxn = 110; const int maxm = 110; int n = 0, m = 0; int f[maxm] = {}; int v[maxn][maxn] = {}, w[maxn][maxn] = {}, s[maxn] = {}; int main() { scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) { scanf("%d", &s[i]); for(int j=1; j<=s[i]; j++) { scanf("%d%d", &v[i][j], &w[i][j]); } } for(int i=1; i<=n; i++) //阶段 { //i和j共同构成状态 for(int j=m; j>=0; j--) { for(int k=1; k<=s[i]; k++) //k是决策 { if(j >= v[i][k]) { f[j] = max(f[j], f[j-v[i][k]] + w[i][k]); } } } } printf("%d", f[m]); return 0; }
二维费用背包
#include <bits/stdc++.h> using namespace std; const int maxn = 60; const int maxm = 410; //二维费用背包 int n = 0, v = 0, m = 0; int a[maxn] = {}, b[maxn] = {}, c[maxn] = {}; int f[maxm][maxm] = {}; int main() { scanf("%d%d%d", &v, &m, &n); for(int i=1; i<=n; i++) { scanf("%d%d%d", &a[i], &b[i], &c[i]); } for(int i=1; i<=n; i++) { for(int j=v; j>=a[i]; j--) { for(int k=m; k>=b[i]; k--) { f[j][k] = max(f[j][k], f[j-a[i]][k-b[i]] + c[i]); } } } printf("%d", f[v][m]); return 0; }
混合背包(将背包都使用二进制拆分转成0/1背包计算)
#include<bits/stdc++.h> using namespace std; int m,n,cnt; int v[100000],w[100100],s[101000],f[100050]; int main(){ int vi=0,wi=0,ni=0; scanf("%d%d",&m,&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&vi,&wi,&ni); if(ni>m/vi||ni==0) ni=m/vi; for(int j=1;j<=ni;j<<=1){ v[++cnt]=j*vi; w[cnt]=j*wi; ni-=j; } if(ni>0){ v[++cnt]=ni*vi; w[cnt]=ni*wi; } } for(int i=1;i<=cnt;i++){ for(int j=m;j>=v[i];j--){ f[j]=max(f[j],f[j-v[i]]+w[i]); } } printf("%d",f[m]); return 0; }