多重背包问题

来源:互联网 发布:热云数据融资 编辑:程序博客网 时间:2024/05/29 15:12
/*Name: 多重背包问题Copyright: Author: Date: 28-07-17 10:24Description: 多重背包问题:在n种物品中选取若干件(第i种物品最多选N[i]次)放在容量为c的背包里,分别用P[i]和W[i]存储第i种物品的价值和重量。求解怎么装物品可使背包里物品总价值最大。输入第一行2个数n和c,表示共有n种物品,背包总容量为c 接下来n行,每行3个数,分别表示第i种物品的重量,价值和最大数量输出一个整数,表示背包里物品最大总价值样例输入5 100080 20 440 50 930 50 740 30 620 20 1样例输出1040算法思想:算法1:二维数组+朴素的穷举思想利用函数式B[i][j] = max(B[i-1][j-K*W[i]]+K*P[i]), 0<=K<=N[i]且0<=K*P[i]<=j,对k进行穷举,找出最大值。该算法与完全背包问题的朴素算法相似,只是多了一个0<=K<=N[i]的条件。算法2:一维数组+朴素的穷举思想 该算法仍然使用穷举思想,但是用一维数组F[j]代替了二维数组B[i][j],也就是未记录行坐标,只记录了列坐标, 与0-1背包问题类似,注意到第i行第j列的元素,由第i-1行的元素决定,且列坐标j大的元素由j小的元素决定,故在同一行中,必须先求出列坐标较大的元素,再求列坐标小的元素,即在内层循环中,应该让循环变量j的值从大到小递减。算法3:二维数组+对第i种物品进行N[i]次0-1选择算法1中,对第i种物品的数量进行穷举,寻找最优解,应该说算法思路是相当清晰的,但是算法1每次都需要判断循环变量k是否满足限定条件,还要先找到最优解bestP,循环结束再执行B1[i][j] = bestP,计算量较大。有一个更为巧妙的思想可以减少部分条件判断语句,那就是把多重背包问题当做多个0-1背包问题来处理,即对第i个物品执行N[i]次0-1选择。这样循环语句for (int k=0; k<N[i]; k++)只需要表示执行N[i]次0-1选择即可,思路更清晰了。算法4:一维数组+对第i种物品进行N[i]次0-1选择和算法2对算法1的降维优化一样,可以用算法4对算法3进行降维优化,而且代码结构与简单的0-1背包问题完全相同,只是在中间插入了一个循环语句for (int k=0; k<N[i]; k++),表示对第i种物品进行N[i]次0-1选择,实在是妙不可言!由此可见,0-1背包问题是多重背包问题的一个特例(N[i]=1),因此他们的代码结构必然是相同的,如果多重背包问题的第2层循环for (int k=0; k<N[i]; k++)只执行一次的话,就变成了一个纯粹的0-1背包问题。 算法5:二进制优化+转化为0-1背包问题 进行二进制优化,把第i种物品拆成重量为W[i]*2^k价值P[i]*2^k的物品,其中满足1+2+...+2^k = N[i]。例如,对N[6]={0,4,9,7,6,1}分别进行拆分,有4=1+2+1, 9=1+2+4+2, 7=1+2+4, 6=1+2+3, 1=1. 此算法需要在输入数据时就对物品进行拆分,相当于增加了新的物品,这样就可以转换为0-1背包问题。 */#include<iostream>#include<cmath>using namespace std;const int MAXC = 6000; //背包最大容量 const int MAXN = 2000; //物品的个数int W[MAXN+1];//物品的重量 int P[MAXN+1];//物品的价值 int N[MAXN+1];//物品的最大数量 int F1[MAXC+1]; //记录装入容量为c的背包的最大价值int B1[MAXN+1][MAXC+1]; //备忘录,记录给定n个物品装入容量为c的背包的最大价值 int F2[MAXC+1]; //记录装入容量为c的背包的最大价值int B2[MAXN+1][MAXC+1]; //备忘录,记录给定n个物品装入容量为c的背包的最大价值 int F3[MAXC+1]; //记录装入容量为c的背包的最大价值int MultiPack_1(int n, int c);//多重背包问题:二维数组+朴素的穷举思想 int MultiPack_2(int n, int c);//多重背包问题:一维数组+朴素的穷举思想 int MultiPack_3(int n, int c);//多重背包问题:二维数组+对第i种物品进行N[i]次0-1选择 int MultiPack_4(int n, int c);//多重背包问题:一维数组+对第i种物品进行N[i]次0-1选择 int ZeroOnePack(int n, int c);//0-1背包问题:一维数组记录最优解int main() {int n, c;cin >> n >> c;//for (int i=1; i<=n; i++)//不计下标为0的元素 //{//cin >> W[i] >> P[i] >> N[i];//}////cout << MultiPack_1(n, c) << endl;//cout << MultiPack_2(n, c) << endl;//cout << MultiPack_3(n, c) << endl;//cout << MultiPack_4(n, c) << endl;//进行二进制优化,把第i种物品拆成重量为W[i]*2^k价值P[i]*2^k的物品,其中满足1+2+...+2^k = N[i]。 //需要在输入数据时就对物品进行拆分,相当于增加了新的物品,这样就可以转换为0-1背包问题 int len = 0;//累计拆分后物品的总数量,因为第i种物品可以选多个,相当于增加了物品的种类,每种物品只有0-1选择 for (int i=0; i<n; i++)  {int w, p, s, t=1;cin >> w >> p >> s;//把s以2的指数幂分堆:1,2,4,...,2^(k-1), s-2^k+1while (s > t)//注意不能写成while (s >= t),否则当s=2^k-1时,会拆分出重量和价值都为0的新物品,虽然不影响最终结果,但效率变低 {W[++len] = w * t;P[len] = p * t;s -= t; //注意s在变小 t *= 2; //t是2的指数幂 }W[++len] = w * s;P[len] = p * s;} //获得新的物品的种类数据后,直接当做0-1背包问题处理 cout << ZeroOnePack(len, c) << endl;return 0;}int MultiPack_1(int n, int c)//多重背包问题:二维数组+朴素的穷举思想 {int bestP; for (int i=1; i<=n; i++){for (int j=1; j<=c; j++) //j递增或递减均可 {//朴素的穷举思想,计算第i个物品装k个时,获得多少价值,存储最大价值到B[i][j] bestP = 0;for (int k=0; k<=N[i] && k*W[i]<=j; k++){if (bestP < B1[i-1][j-k*W[i]] + k*P[i])bestP = B1[i-1][j-k*W[i]] + k*P[i];}B1[i][j] = bestP;}}return B1[n][c];}int MultiPack_2(int n, int c)//多重背包问题:一维数组+朴素的穷举思想 {int bestP; for (int i=1; i<=n; i++){for (int j=c; j>=W[i]; j--) //类似于0-1背包问题,j只能递减{//朴素的穷举思想,计算第i个物品装k个时,获得多少价值,存储最大价值到F[j] bestP = 0;for (int k=0; k<=N[i] && k*W[i]<=j; k++){if (bestP < F1[j-k*W[i]] + k*P[i])bestP = F1[j-k*W[i]] + k*P[i];}F1[j] = bestP;}}return F1[c];}int MultiPack_3(int n, int c)//多重背包问题:二维数组+对第i种物品进行N[i]次0-1选择 {int bestP; for (int i=1; i<=n; i++){for (int k=0; k<N[i]; k++)//对第i种物品进行N[i]次0-1选择 {for (int j=c; j>0; j--) //0-1背包问题,j只能递减,注意与完全背包问题的区别 {if (j < W[i]) //容量不够,则和给定i-1个物品装入容量为j的背包的结果一致 {B2[i][j] = B2[i-1][j];}else //B2[i][j-W[i]]表示给定i个物品装入容量为j-W[i]的背包,质量为W[i]的物品可能已经装了多个 {B2[i][j] = max(B2[i-1][j], B2[i][j-W[i]] + P[i]);}} } }return B1[n][c];}int MultiPack_4(int n, int c)//多重背包问题:一维数组+对第i种物品进行N[i]次0-1选择 { for (int i=1; i<=n; i++){for (int k=0; k<N[i]; k++)//对第i种物品进行N[i]次0-1选择 {//须先求出列坐标j较大的元素,故让循环变量j的值从大到小递减for (int j=c; j>=W[i]; j--){//当(j < W[i] || F2[j] > F2[j-W[i]] + P[i])时,F2[j]的值不变if (F2[j] < F2[j-W[i]] + P[i])F2[j] = F2[j-W[i]] + P[i];}}}return F2[c];}int ZeroOnePack(int n, int c)//0-1背包问题:一维数组记录最优解{ for (int i=1; i<=n; i++){//须先求出列坐标j较大的元素,故让循环变量j的值从大到小递减for (int j=c; j>=W[i]; j--){//当(j < W[i] || F3[j] > F3[j-W[i]] + P[i])时,F3[j]的值不变if (F3[j] < F3[j-W[i]] + P[i])F3[j] = F3[j-W[i]] + P[i];}}return F3[c];}

原创粉丝点击