0-1背包问题(算法进化历程)

来源:互联网 发布:淘宝店铺如何取消分流 编辑:程序博客网 时间:2024/06/03 18:54
/*Name: 0-1背包问题(算法进化历程) Copyright: Author: Date: 24-07-17 21:10Description: 7113_Charm Bracelet查看 提交 统计 提问总时间限制: 1000ms 内存限制: 65536kB描述Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from the N(1 ≤N≤3,402) available charms. Each charm iin the supplied list has a weight Wi(1 ≤Wi≤400), a 'desirability' factor Pi(1 ≤Pi≤100),  and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M(1 ≤M≤12,880).Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.输入Line 1: Two space-separated integers: N and MLines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di输出Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints题意:经典的0-1背包问题,有N(1 ≤N≤3402)个物品,编号为i的物品的重量为Wi(1 ≤Wi≤400),价值为Pi(1 ≤Pi≤100),现在要从这些物品中选择一些物品装到一个容量为C(1 ≤C≤12880) 的背包中,使得背包内物品在总重量不超过M的前提下价值尽量大。输入第1行:分别代表物品数量和背包容量的两个整数N和C第2..N+1行:每行两个整数Wi和Di,分别表示第i个物品的重量和价值输出 一个整数,表示最大的总价值。 样例输入4 61 42 63 122 7样例输出231.题目分析:考虑到每种物品只有2种选择,即装入背包或不装入背包,并且物品数和背包容量已给定,要计算装入背包物品的最大价值和最优装入方案,可用动态规划算法思想。2.算法设计:物品有n种,背包容量为C,分别用p[i]和w[i]存储第i种物品的价值和重量,用x[i]标记第i种物品是否装入背包,用数组p[n][c]记录给定n个物品装入容量为c的背包的最大价值。 动态规划有两种基本思路,一种是自顶而下的备忘录算法,它采用递归的方式,一步步缩小问题的规模,找到边界(n==0)以后,处理好边界(bestP = (c >= W[n]) ? P[n] : 0;),然后一步步返回,返回更大问题的解。每获得一个子问题的答案,便保存到备忘录数组p[n][c],避免下次充分计算,提升了效率。另一种思路是自底向上的动态规划算法,它从最小的子问题(只包含0号物品)开始处理,然后依次增加可装物品的数量,用数组p[n][c]记录每一个已处理子问题的答案,由于0-1背包问题具有最优子结构,每个问题的最优解都是由其子问题的最优解组成,故这样依次递增问题的规模,可以得到最终需要的最优解。  3. 复杂度分析:无论是自顶向下的备忘录算法还是自底向上的动态规划算法,都可以在O(n^3)时间内求解。他们有O(n^2)个备忘记录项(或子问题),这些记录项的初始化耗费O(n^2)时间。每个记录项只填入一次,每次填入时,耗费O(n)时间。因而填入O(n^2)各记录项总共耗费O(n^3)时间。对每个子问题,两种方法都只解一次,并记录答案,再碰到该子问题时,不重新求解而是取用已有答案。它们节省了计算量,提高了算法的效率。自底向上的动态规划算法按顺序计算了每个子问题的解;自顶向下的备忘录算法采用递归的方式只计算了确实需要求解的子问题,效率更高(当然,由于需要调用递归函数,有了一些额外的开销),也更便于理解。 改进的动态规划算法:算法3:使用动态规划算法时,注意到 B[i][j] = max(B[i-1][j], B[i-1][j-W[i]]+P[i]);故可以用两个一维数组代替二维数组。 算法4:在算法3的基础上进一步优化,注意到总是由下标较小的元素来生成下标较大的元素,故可以让第二层循环的循环变量j由大到小,这样就可以用一个一维数组B[]来代替pre[]和cur[]来存储解了,即数组B中下标较小的元素B[j-W[i]]相当于pre[j-W[i]],而B[j]相当于cur[j],当(j < W[i] || B[j] > B[j-W[i]] + P[i])时,B[j]的值不变,否则B[j] = B[j-W[i]] + P[i];。 */#include<iostream>#include<cmath>using namespace std;const int MAXC = 12880; //背包最大容量 const int MAXN = 3402; //物品的个数int W[MAXN+1];//物品的重量 int P[MAXN+1];//物品的价值 int pre[MAXC+1]; //记录上一行元素值int cur[MAXC+1]; //记录当前行元素值int B3[MAXC+1]; //记录最优解 int B1[MAXN+1][MAXC+1]; //备忘录,记录给定N个物品装入容量为C的背包的最大价值 int B2[MAXN+1][MAXC+1]; //备忘录,记录给定N个物品装入容量为C的背包的最大价值 int Best_1(int n, int c); //计算给定n个物品装入容量为c的背包的最大价值  int Best_2(int n, int c);//动态规划:自底而上,获得给定n个物品装入容量为c的背包的最大价值 int Best_3(int n, int c);//优化的动态规划算法int Best_4(int n, int c);//进一步优化的动态规划算法int main() {int n, c;cin >> n >> c;for (int i=1; i<=n; i++)//不计下标为0的元素 {cin >> W[i] >> P[i];}for (int i=1; i<=n; i++)//初始化为-1,表示还没有存储该备忘录  { for (int j=1; j<=c; j++) { B1[i][j] = -1;}}cout << Best_1(n, c) << endl;cout << Best_2(n, c) << endl;cout << Best_3(n, c) << endl;cout << Best_4(n, c) << endl;return 0;}int Best_1(int n, int c) //计算给定n个物品装入容量为c的背包的最大价值  { if (B1[n][c] != -1)  //如果这个问题曾经计算过,直接返回  {return B1[n][c];}int bestP = 0;if (n == 1)//处理第1个物品,即只有一个物品 {bestP = (c >= W[n]) ? P[n] : 0;}else{bestP = Best_1(n-1, c); //先计算不装第n个物品的情形 if (c >= W[n])//如果装得下,从装和不装两者中去最大值 {    bestP = max(bestP, Best_1(n-1, c-W[n])+P[n]);}}B1[n][c] = bestP;//做备忘录     return bestP;}int Best_2(int n, int c)//动态规划:自底而上,获得给定n个物品装入容量为c的背包的最大价值 {//记录前i(i>=1)个物品装入容量为0-c的背包的最大价值  for (int i=1; i<n; i++){for (int j=0; j<=c; j++){B2[i][j] = B2[i-1][j]; if (j >= W[i] && B2[i-1][j] < B2[i-1][j-W[i]] + P[i])B2[i][j] = B2[i-1][j-W[i]] + P[i];}}//第n个物品只需考虑容量为c的一种情况B2[n][c] = B2[n-1][c]; //先默认为不装第n个物品 if (c >= W[n]) //如果容量足够装第n个物品 {B2[n][c] = max(B2[n-1][c], B2[n-1][c-W[n]]+P[n]);}return B2[n][c];}int Best_3(int n, int c)//优化的动态规划算法,使用2个一维数组代替二维数组 {//记录前i(i>=1)个物品装入容量为0-c的背包的最大价值  for (int i=1; i<n; i++){for (int j=0; j<=c; j++){if (j < W[i] || pre[j] > pre[j-W[i]] + P[i])cur[j] = pre[j]; elsecur[j] = pre[j-W[i]] + P[i];}for (int j=0; j<=c; j++){pre[j] = cur[j]; }}//第n个物品只需考虑容量为c的一种情况cur[c] = pre[c]; //先默认为不装第n个物品 if (c >= W[n] && pre[c] < pre[c-W[n]] + P[n]) //如果容量足够装第n个物品 {cur[c] = pre[c-W[n]] + P[n];}return cur[c];}int Best_4(int n, int c)//优化的动态规划算法,使用1个一维数组代替二维数组 {//记录前i(i>=1)个物品装入容量为0-c的背包的最大价值  for (int i=1; i<n; i++){for (int j=c; j>0; j--){//当(j < W[i] || B3[j] > B3[j-W[i]] + P[i])时,B3[j]的值不变if (j >= W[i] && B3[j] < B3[j-W[i]] + P[i])B3[j] = B3[j-W[i]] + P[i];}}//第n个物品只需考虑容量为c的一种情况if (c >= W[n] && B3[c] < B3[c-W[n]] + P[n]) //如果容量足够装第n个物品 {B3[c] = B3[c-W[n]] + P[n];}return B3[c];}

原创粉丝点击