首先,我们知道01背包的状态转换方程:f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }
f[i,j]表示在前i件物品中选择若干件放在承重为 j 的背包中,可以取得的最大价值。 Pi表示第i件物品的价值。 决策:为了背包中物品总价值最大化,第 i件物品应该放入背包中吗 ? 题目描述: 有编号分别为a,b,c,d,e的五件物品,它们的重量分别是4、5、6、2、2,它们的价值分别是6、4、5、3、6,现在给你个承重为10的背包, 如何让背包里装入的物品具有最大的价值总和? 通过上表可以找出01背包问题的规律:
比如第a4,代表承重为4的背包可以装入价值6的物品,b9,代表承重为9的背包可以装入a,b各一个,价值总共为10.
01背包问题:
#include<iostream> using namespace std; #define V 1500 unsigned int f[10][V];//全局变量,自动初始化为0 unsigned int weight[10]; unsigned int value[10]; #define max(x,y) (x)>(y)?(x):(y) int main() { int N,M; cin>>N;//物品个数 cin>>M;//背包容量 for (int i=1;i<=N; i++) { cin>>weight[i]>>value[i]; } for (int i=1; i<=N; i++) for (int j=1; j<=M; j++) { if (weight[i]<=j) { f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]); } else f[i][j]=f[i-1][j]; } cout<<f[N][M]<<endl;//输出最优解 }完全背包问题 一个背包总容量为V,现在有N个物品,第i个 物品体积为weight[i],价值为value[i],每个物品都有无限多件,现在往背包里面装东西,怎么装能使背包的内物品价值最大?
对比一下,看到的区别是,完全背包问题中,物品有无限多件。往背包里面添加物品时,只要当前背包没装满,可以一直添加。那么状态转移方程为:
f[i+1][j]=max(f[i][j-k*weight[i+1]+k*value[i+1]),其中0<=k<=V/weight[i+1]
#include<iostream> using namespace std; #define V 1500 unsigned int f[V];//全局变量,自动初始化为0 unsigned int weight[10]; unsigned int value[10]; #define max(x,y) (x)>(y)?(x):(y) int main() { int N,M; cin>>N;//物品个数 cin>>M;//背包容量 for (int i=1;i<=N; i++) { cin>>weight[i]>>value[i]; } for (int i=1; i<=N; i++) for (int j=1; j<=M; j++) { if (weight[i]<=j) { f[j]=max(f[j],f[j-weight[i]]+value[i]); } } cout<<f[M]<<endl;//输出最优解 } 多重背包问题:
多重背包问题要求很简单,就是每件物品给出确定的件数,求 可得到的最大价值 多重背包转换成 01 背包问题就是多了个初始化,把它的件数C 用二进制 分解成若干个件数的集合,这里面数字可以组合成任意小于等于C 的件数,而且不会重复,之所以叫二进制分解,是因为这样分解可 以用数字的二进制形式来解释 比如:7的二进制 7 = 111 它可以分解成 001 010 100 这三个数可以 组合成任意小于等于7 的数,而且每种组合都会得到不同的数 15 = 1111 可分解成 0001 0010 0100 1000 四个数字 如果13 = 1101 则分解为 0001 0010 0100 0110 前三个数字可以组合成 7以内任意一个数,即1、2、4可以组合为1——7内所有的数,加上 0110 = 6 可以组合成任意一个大于6 小于等于13 的数,比如12,可以让前面贡献6且后面也贡献6就行了。虽然有重复但总是能把 13 以内所有的数都考虑到了,基于这种 思想去把多件物品转换为,多种一件物品,就可用01 背包求解了。
看代码:
int n; //输入有多少种物品 int c; //每种物品有多少件 int v; //每种物品的价值 int s; //每种物品的尺寸 int count = 0; //分解后可得到多少种物品 int value[MAX]; //用来保存分解后的物品价值 int size[MAX]; //用来保存分解后物品体积 scanf("%d", &n); //先输入有多少种物品,接下来对每种物品进行分解 while (n--) //接下来输入n中这个物品 { scanf("%d%d%d", &c, &s, &v); //输入每种物品的数目和价值 for (int k=1; k<=c; k<<=1) //<<右移 相当于乘二 { value[count] = k*v; size[count++] = k*s; c -= k; } if (c > 0) { value[count] = c*v; size[count++] = c*s; } } 使用二进制优化后的多重背包:
#include <iostream> #include <stdio.h> #include <string.h> using namespace std; int main(void) { int Limit,nKind,i,j,k, v[111],w[111],c[111],dp[111]; //v[]存价值,w[]存尺寸,c[]存件数 //在本题中,价值是米的重量,尺寸是米的价格 int count,Value[1111],size[1111]; //count存储分解完后的物品总数 //Value存储分解完后每件物品的价值 //size存储分解完后每件物品的尺寸 count=0; cin>>Limit>>nKind; for(i=0; i<nKind; i++) { cin>>w[i]>>v[i]>>c[i]; //对该种类的c[i]件物品进行二进制分解 for(j=1; j<=c[i]; j<<=1) { //<<左移1位,相当于乘2 Value[count]=j*v[i]; size[count++]=j*w[i]; c[i]-=j; } if(c[i]>0) { Value[count]=c[i]*v[i]; size[count++]=c[i]*w[i]; } } //经过上面对每一种物品的分解, //现在Value[]存的就是分解后的物品价值 //size[]存的就是分解后的物品尺寸 //count就相当于原来的n //下面就直接用01背包算法来解 memset(dp,0,sizeof(dp)); for(i=0; i<count; i++) for(j=Limit; j>=size[i]; j--) if(dp[j]<dp[j-size[i]]+Value[i]) dp[j]=dp[j-size[i]]+Value[i]; cout<<dp[Limit]<<endl; return 0; } 为优化的:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int Value[105]; int Cost[105]; int Bag[105]; int dp[105]; int main() { int C,m,n; scanf("%d",&C); while(C--) { scanf("%d%d",&n,&m); for(int i = 1; i <= m; i++) scanf("%d%d%d",&Cost[i],&Value[i],&Bag[i]); memset(dp,0,sizeof(dp)); for(int i=1; i<= m; i++) for(int j=1; j<=Bag[i]; j++) for(int k=n; k>=Cost[i]; k--) dp[k]=max(dp[k], dp[k-Cost[i]]+Value[i]); printf("%d\n",dp[n]); } return 0; }
