背包问题
关于讲解详见dd大牛的背包九讲
我就存一下我自己写的代码吧。。有一些自己的理解
1.01背包
二维
//01背包最基础版 /* 样例 11 6 2 4 5 6 10 3 1 7 4 5 11 1 */ /* 注意: 如果一定要装满 则在初始化的时候f[0]=0; 其他的f都为-inf */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define N 1000 using namespace std; int f[N][N]; int v[N],w[N]; int main(){ memset(f,0,sizeof(f)); int n,V; scanf("%d%d",&V,&n); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++)//f[i][j]代表前i件物品放入容积为j所产生的最大价值 for(int j=0;j<=V;j++){ f[i][j]=f[i-1][j]; if(j>=w[i]) f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]); } for(int i=1;i<=n;i++){ for(int j=0;j<=V;j++) printf("%d ",f[i][j]); printf(" "); } return 0; }一维
//01背包一维数组 //考虑到当前状态只和前一层有关 所以以一维数组进行优化空间复杂度 //唯一改变的是内层循环j改成逆序 因为当前层存的是上一层的 状态由上层转移过来 /* 样例 11 6 2 4 5 6 10 3 1 7 4 5 11 1 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define N 1000 using namespace std; int f[N]; int v[N],w[N]; int main(){ memset(f,0,sizeof(f)); int n,V; scanf("%d%d",&V,&n); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++)//f[i][j]代表前i件物品放入容积为j所产生的最大价值 for(int j=V;j>=w[i];j--)//逆序! j最小只用到w[i] 因为小于w[i]就放不下了 就是上一层的答案 f[j]=max(f[j],f[j-w[i]]+v[i]); for(int i=0;i<=V;i++) printf("%d ",f[i]); return 0; }
2.完全背包
一维
/* 11 6 2 4 5 6 10 3 1 7 4 5 11 1 */ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #define N 1000 using namespace std; int f[N]; int v[N],w[N]; int main(){ memset(f,0,sizeof(f)); int n,V; scanf("%d%d",&V,&n); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++) for(int j=w[i];j<=V;j++) f[j]=max(f[j],f[j-w[i]]+v[i]); printf("%d",f[V]); return 0; }
二维
/* 11 6 2 4 5 6 10 3 1 7 4 5 11 1 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define N 1000 using namespace std; int f[N][N]; int v[N],w[N]; int main(){ memset(f,0,sizeof(f)); int n,V; scanf("%d%d",&V,&n); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++) for(int j=1;j<=V;j++) { f[i][j]=f[i-1][j]; for(int k=1;k<=j/w[i];k++){ if(j>=k*w[i]) f[i][j]=max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]); } } for(int i=1;i<=n;i++){ for(int j=0;j<=V;j++) printf("%d ",f[i][j]); printf(" "); } return 0; }
3.多重背包
//http://acm.hdu.edu.cn/showproblem.php?pid=2191 //多重背包模板题 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int f[1005],p[1005],h[1005],c[1005]; int main(){ int T; scanf("%d",&T); int n,m; while(T--){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&p[i],&h[i],&c[i]); for(int i=1;i<=m;i++) for(int k=1;k<=c[i];k++) for(int j=n;j>=p[i];j--) f[j]=max(f[j],f[j-p[i]]+h[i]); printf("%d ",f[n]); memset(f,0,sizeof(f)); } }改进版代码
/** 此做法时间复杂度大大降低 虽然还不会 * 多重背包: * 有N种物品和一个容量为V的背包。第i种物品最多有Mi件可用, * 每件耗费的空间是Ci,价值是Wi。 * 求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。 */ #include <stdio.h> #include <string.h> int max(int a, int b){ if (a > b)return a; return b; } #define maxn 100005 int c[maxn], w[maxn], num[maxn];//c:费用 w:价值 num:数量 int dp[maxn]; int V; //V:容量 V1:容量2 //01背包 void ZeroOnePack(int c, int w) { for (int v = V; v >= c; v--) { dp[v] = max(dp[v], dp[v - c] + w); } } //完全背包 void CompletePack(int c, int w) { for (int v = c; v <= V; v++) { dp[v] = max(dp[v], dp[v - c] + w); } } //多重背包 void MultiplePack(int c, int w, int num) { if (c * num >= V) { CompletePack(c, w); } else { int k = 1; while (k < num) { ZeroOnePack(k*c, k*w); num -= k; k <<= 1; } ZeroOnePack(num*c, num*w); } } int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d%d", &V, &n); for (int i = 1; i <= n; i++) scanf("%d%d%d", &c[i], &w[i], &num[i]); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; i++) MultiplePack(c[i], w[i], num[i]); printf("%d ", dp[V]); } return 0; } /* 1 10 5 5 1 1 4 2 1 3 3 1 2 4 1 1 5 1 ** 14 */
核心代码部分
0/1背包 二维实现: for(int i=1;i<=n;i++) for(int j=1;j<=v;j++) { f[i][j]=f[i-1][j]; if((j>=w[i])&&(f[i][j]<f[i-1][j-w[i]]+p[i]))f[i][j]=f[i-1][j-w[i]]+p[i]; } cout<<f[n][v]; 一维实现: for(int i=1;i<=n;i++) for(int j=v;j>=w[i];j--) if(f[j]<f[j-w[i]]+p[i])f[j]=f[j-w[i]]+p[i]; cout<<f[v]; 完全背包: 二维实现: for(int i=1;i<=n;i++) for(int j=1;j<=v;j++) for(int k=0;k<=j/w[i];k++) if((j>=k*w[i])&&(f[i][j]<f[i-1][j-k*w[i]]+k*p[i])) f[i][j]=f[i-1][j-k*w[i]]+k*p[i]; cout<<f[n,v]; 一维实现: for(int i=1;i<=n;i++) for(int j=1;j<=v;j++) for(int k=0;k<=j/w[i];k++) if((j>=w[i]*k)&&(f[j]<f[j-k*w[i]]+k*p[i])) f[j]=f[j-k*w[i]]+k*p[i]; cout<<f[v]; 多重背包: 与完全背包相似,只是决策不同,即k:=0 to n[i] do (n[i]即第i件物品的数量)