0-1背包问题

合集下载

回溯法解决0-1背包问题

回溯法解决0-1背包问题

回溯法解决0-1背包问题问题描述: 有n件物品和⼀个容量为c的背包。

第i件物品的价值是v[i],重量是w[i]。

求解将哪些物品装⼊背包可使价值总和最⼤。

所谓01背包,表⽰每⼀个物品只有⼀个,要么装⼊,要么不装⼊。

回溯法: 01背包属于找最优解问题,⽤回溯法需要构造解的⼦集树。

在搜索状态空间树时,只要左⼦节点是可⼀个可⾏结点,搜索就进⼊其左⼦树。

对于右⼦树时,先计算上界函数,以判断是否将其减去,剪枝啦啦!上界函数bound():当前价值cw+剩余容量可容纳的最⼤价值<=当前最优价值bestp。

为了更好地计算和运⽤上界函数剪枝,选择先将物品按照其单位重量价值从⼤到⼩排序,此后就按照顺序考虑各个物品。

#include <stdio.h>#include <conio.h>int n;//物品数量double c;//背包容量double v[100];//各个物品的价值double w[100];//各个物品的重量double cw = 0.0;//当前背包重量double cp = 0.0;//当前背包中物品价值double bestp = 0.0;//当前最优价值double perp[100];//单位物品价值排序后int order[100];//物品编号int put[100];//设置是否装⼊//按单位价值排序void knapsack(){int i,j;int temporder = 0;double temp = 0.0;for(i=1;i<=n;i++)perp[i]=v[i]/w[i];for(i=1;i<=n-1;i++){for(j=i+1;j<=n;j++)if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]{temp = perp[i];perp[i]=perp[i];perp[j]=temp;temporder=order[i];order[i]=order[j];order[j]=temporder;temp = v[i];v[i]=v[j];v[j]=temp;temp=w[i];w[i]=w[j];w[j]=temp;}}}//回溯函数void backtrack(int i){double bound(int i);if(i>n){bestp = cp;return;}if(cw+w[i]<=c){cw+=w[i];cp+=v[i];put[i]=1;backtrack(i+1);cw-=w[i];cp-=v[i];}if(bound(i+1)>bestp)//符合条件搜索右⼦数backtrack(i+1);}//计算上界函数double bound(int i){double leftw= c-cw;double b = cp;while(i<=n&&w[i]<=leftw){leftw-=w[i];b+=v[i];i++;}if(i<=n)b+=v[i]/w[i]*leftw;return b;}int main(){int i;printf("请输⼊物品的数量和容量:");scanf("%d %lf",&n,&c);printf("请输⼊物品的重量和价值:");for(i=1;i<=n;i++){printf("第%d个物品的重量:",i);scanf("%lf",&w[i]);printf("价值是:");scanf("%lf",&v[i]);order[i]=i;}knapsack();backtrack(1);printf("最有价值为:%lf\n",bestp);printf("需要装⼊的物品编号是:");for(i=1;i<=n;i++){if(put[i]==1)printf("%d ",order[i]);}return 0;}时间复杂度分析: 上界函数bound()需要O(n)时间,在最坏的情况下有O(2^n)个右⼦结点需要计算上界,回溯算法backtrack需要的计算时间为O(n2^n)。

0_1背包问题的多种解法

0_1背包问题的多种解法

一、 问题描述0/1背包问题:现有n 种物品,对1<=i<=n ,已知第i 种物品的重量为正整数W i ,价值为正整数V i ,背包能承受的最大载重量为正整数W ,现要求找出这n 种物品的一个子集,使得子集中物品的总重量不超过W 且总价值尽量大。

(注意:这里对每种物品或者全取或者一点都不取,不允许只取一部分)二、 算法分析根据问题描述,可以将其转化为如下的约束条件和目标函数:)2(max )1()1}(1,0{11∑∑==⎪⎩⎪⎨⎧≤≤∈≤ni i i ini i i x v n i x Wx w 于是,问题就归结为寻找一个满足约束条件(1),并使目标函数式(2)达到最大的解向量),......,,,(321n x x x x X =。

首先说明一下0-1背包问题拥有最优解。

假设),......,,,(321n x x x x 是所给的问题的一个最优解,则),......,,(32n x x x 是下面问题的一个最优解:∑∑==⎪⎩⎪⎨⎧≤≤∈-≤ni i i ini i i x v n i x x w W x w 2211max )2}(1,0{。

如果不是的话,设),......,,(32n y y y 是这个问题的一个最优解,则∑∑==>n i ni ii ii xv y v 22,且∑=≤+ni iiW yw x w 211。

因此,∑∑∑====+>+ni i i n i n i i i i i x v x v x v y v x v 1221111,这说明),........,,,(321n y y y x 是所给的0-1背包问题比),........,,,(321n x x x x 更优的解,从而与假设矛盾。

穷举法:用穷举法解决0-1背包问题,需要考虑给定n 个物品集合的所有子集,找出所有可能的子集(总重量不超过背包重量的子集),计算每个子集的总重量,然后在他们中找到价值最大的子集。

分支界限法0-1背包问题(优先队列式分支限界法)

分支界限法0-1背包问题(优先队列式分支限界法)

分⽀界限法0-1背包问题(优先队列式分⽀限界法)输⼊要求有多组数据。

每组数据包含2部分。

第⼀部分包含两个整数C (1 <= C <= 10000)和 n (1 <= n <= 10,分别表⽰背包的容量和物品的个数。

第⼆部分由n⾏数据,每⾏包括2个整数 wi(0< wi <= 100)和 vi(0 < vi <= 100),分别表⽰第i个物品的总量和价值输出要求对于每组输⼊数据,按出队次序输出每个结点的信息,包括所在层数,编号,背包中物品重量和价值。

每个结点的信息占⼀⾏,如果是叶⼦结点且其所代表的背包中物品价值⼤于当前最优值(初始为0),则输出当前最优值 bestv 和最优解bestx(另占⼀⾏)参见样例输出测试数据输⼊⽰例5 32 23 22 3输出⽰例1 1 0 02 2 2 23 5 2 24 10 4 5bestv=5, bestx=[ 1 0 1 ]4 11 2 23 4 5 42 3 0 0⼩贴⼠可采⽤如下的结构体存储结点:typedef struct{int no; // 结点在堆中的标号int sw; // 背包中物品的重量int sv; // 背包中物品的价值double prior; // 优先值 sv/sw}Node;#include<stdio.h>#include<math.h>#include<string.h>typedef struct {int no; // 结点标号int id; // 节点idint sw; // 背包中物品的重量int sv; // 背包中物品的价值double prior; // sv/sw}Node;int surplusValue(int *v,int n,int y) {int sum = 0;for(int i = y; i <= n; i++) {sum += v[i];}return sum;}void qsort(Node *que,int l,int r) {int len = r - l + 1;int flag;for(int i = 0; i < len; i ++) {flag = 0;for(int j = l; j < l + len - i; j++) {if(que[j].prior < que[j+1].prior) {Node t = que[j];que[j] = que[j+1];que[j+1] = t;flag = 1;}}//if(!flag ) return;}}void branchknap(int *w,int *v,int c,int n) {int bestv = 0;int f = 0;int r = 0;Node que[3000];memset(que,0,sizeof(que));int path[15];que[0].no = 1;que[0].id = que[0].sv = que[0].sw = que[0].prior = 0;while(f <= r) {Node node = que[f];printf("%d %d %d %d\n",node.id+1,node.no,node.sw,node.sv);if(node.no >= pow(2,n)) {if(node.sv > bestv) {bestv = node.sv;printf("bestv=%d, bestx=[",bestv);int temp = node.no;int i = 0;while(temp > 1) {if(temp % 2 == 0)path[i] = 1;elsepath[i] = 0;temp /= 2;i++ ;}i--;while(i >= 0) {while(i >= 0) {printf(" %d",path[i]);i--;}printf(" ]\n");}} else {if((node.sw + w[node.id + 1]) <= c && surplusValue(v,n,node.id+1) + node.sv > bestv) { r++;que[r].id = node.id + 1;que[r].no = node.no*2;int id = node.id + 1;que[r].sv = node.sv + v[id];que[r].sw = node.sw + w[id];que[r].prior = que[r].sv / (que[r].sw*1.0);}if(surplusValue(v,n,node.id+2) + node.sv > bestv) {r++;que[r].id = node.id + 1;que[r].no = node.no*2 + 1;que[r].sv = node.sv;que[r].sw = node.sw;que[r].prior = node.prior;}}f++;qsort(que,f,r);}}int main() {int c,n;int w[15],v[15];while(~scanf("%d %d",&c,&n)){for(int i = 1; i <= n; i++) {scanf("%d %d",&w[i],&v[i]);}branchknap(w,v,c,n);}return 0;}#include<stdio.h>#include<math.h>#include<string.h>typedef int bool;#define true 1#define false 0struct Node{int no; // ?áµ?±êo?int id; //jie dian idint sw; // ±3°ü?D·µá?int sv; // ±3°ü?D·µ?µdouble prior;};struct Node queuee[2000];int w[15],v[15];int bestv = 0,c,n;int path[15]; //lu jingint surplusValue(int y) {int sum = 0;for(int i = y; i <= n; i++)sum += v[i];return sum;}void qsort(int l,int r) {// printf("------\n");int len = r - l + 1;//printf("----%d %d %d-----\n",l,r,len);bool flag;for(int i = 0; i < len ; i++) {flag = false;for(int j = l; j <l+ len -i ;j++) {if(queuee[j].prior < queuee[j+1].prior) {struct Node temp = queuee[j];queuee[j] = queuee[j+1];queuee[j+1] = temp;flag = true;}//if(!flag) return;}}// printf("---排序嘻嘻---\n");//for(int i = l; i <= r;i++ )// printf("***%d : %.2lf\n",queuee[i].no,queuee[i].prior);// printf("\n------\n");}void branchknap() {bestv = 0;int f = 0;int r = 0;queuee[0].no = 1;queuee[0].id = 0;queuee[0].sv = 0;queuee[0].sw = 0;queuee[0].prior = 0;// printf("f: %d r: %d\n",f,r);while(f <= r) {struct Node node = queuee[f];printf("%d %d %d %d\n",node.id+1,node.no,node.sw,node.sv);if(node.no >= pow(2,n)) {if(node.sv > bestv) {bestv = node.sv;//TODOprintf("bestv=%d, bestx=[",bestv);int temp = node.no;int i = 0;while(temp > 1) {if(temp%2 == 0)path[i] = 1;elsepath[i] = 0;temp /= 2;i++;}i--;while(i >= 0) {while(i >= 0) {printf(" %d",path[i]);i--;}printf(" ]\n");}} else {if((node.sw + w[node.id+1]) <= c && surplusValue(node.id+1) + node.sv > bestv) { r++;//printf("%d\n",(node.sw + w[node.id+1]));queuee[r].id = node.id+1;queuee[r].no = node.no*2;int id = node.id+1;queuee[r].sv = node.sv + v[id];queuee[r].sw = node.sw + w[id];queuee[r].prior = queuee[r].sv/(queuee[r].sw*1.0);//printf("进队id: %d\n",queuee[r].no) ;//printf("%d %d %d\n",id,v[id], w[id]);}if(surplusValue(node.id+2) + node.sv > bestv) {r++;queuee[r].id = node.id+1;queuee[r].no = node.no*2 + 1;queuee[r].sv = node.sv ;queuee[r].sw = node.sw ;queuee[r].prior = node.prior;//printf("进队id: %d\n",queuee[r].no) ;}}f++;qsort(f,r);}}int main() {while(~scanf("%d %d",&c,&n)){memset(queuee,0,sizeof(queuee));for(int i = 1; i <= n; i++) {scanf("%d %d",&w[i],&v[i]);}branchknap();}return 0;}。

0-1背包问题

0-1背包问题
• 从每种物品的角度考虑,求解完全背包问题的策略 由对某种物品取或者不取变成了取0件、取1件、取2 件,……等很多种。按基本的01背包问题求解算法 的思路,设f[i][v]表示前i种物品恰放入一个载荷能力 为v的背包的最大价值。则状态转移方程为 f[i][v]=max{ f[i-1][v-k*w[i]]+k*p[i] | 0k*p[i]v }。
• 因为本题的目标是求解储蓄罐内可以达到的最小金 额,所以初始化时所有dp都为INF(无穷大),且 dp[0]=0。(如果要求解储蓄罐内可以达到的最大金 额,那么应该初始化为-1)。本题的解为dp[n][m], 如果dp[n][m]为INF,则说明m克是一个不可达的状 态。
• 本题dp数组一维,下标为硬币重量。
SPACE ELEVATOR
• 试题来源:USACO 2005 March Gold • 在线测试:POJ 2392
• 奶牛们要上太空了!它们计划建造一座太空电梯作 为它们登上太空的轨道:电梯是一个巨大的、由块 组成的塔,有K(1K400)种不同类型的块用于建 造塔。类型i的块的高度为hi(1hi100),块的数量 为ci(1ci10)。由于宇宙射线可能造成的损害,在 塔中,由类型i的块组成的部分不能超过最大高度ai (1ai40000)。
• 由于本题的数据范围较大,而long long可以存 储19位,因此,将超过19位的部分称为高位部 分(如果存在),19位以内的部分称为低位部 分。设两个long long类型的变量dp1[ ]和dp2[ ], 其中dp1[ ]存储高位部分,dp2[ ]存储低位部分。 这样,就可以按照题目要求的数据规模存储和 输出方式数了。
多重背包
• 多重背包问题描述如下:给定n种物品和一个 载荷能力为M的背包,物品i重量为wi,数量为 numi , 价 值 为 pi , 其 中 wi>0 , pi>0 , numi>0 , 1in;求解将哪些物品装入背包,可使得使 背包里所放物品的总重量不超过M,且背包中 物品的价值总和达到最大。

0-1背包问题课程设计

0-1背包问题课程设计

0-1背包问题课程设计一、课程目标知识目标:1. 学生理解0-1背包问题的定义,掌握其数学模型及相关概念。

2. 学生掌握0-1背包问题的动态规划解法,并能够运用此方法解决相关问题。

3. 学生了解0-1背包问题在实际生活中的应用,如资源分配、行程规划等。

技能目标:1. 学生能够运用数学语言描述0-1背包问题,并建立相应的数学模型。

2. 学生通过编程实践,掌握动态规划算法在0-1背包问题中的应用。

3. 学生具备分析问题、提出解决方案并优化方案的能力。

情感态度价值观目标:1. 培养学生面对问题时的积极态度,激发学生解决问题的兴趣和热情。

2. 增强学生团队合作意识,培养沟通协调能力。

3. 使学生认识到数学建模在解决实际问题中的重要性,提高学生的应用意识。

课程性质:本课程为高中数学选修课程,结合计算机科学领域的内容,旨在培养学生的跨学科素养。

学生特点:高中生具备一定的数学基础和逻辑思维能力,对实际问题有一定的探索欲望。

教学要求:教师需引导学生从实际问题出发,通过数学建模、算法设计等环节,培养学生的分析问题和解决问题的能力。

在教学过程中,注重理论与实践相结合,关注学生的个性发展。

通过本课程的学习,使学生能够达到上述课程目标,并在实际操作中展示具体的学习成果。

二、教学内容本课程教学内容主要包括以下几部分:1. 0-1背包问题基本概念与数学模型:介绍0-1背包问题的定义、特性以及与实际生活的联系。

结合教材相关章节,让学生掌握数学建模的基本方法。

2. 动态规划原理:讲解动态规划的基本思想、原理及其在0-1背包问题中的应用。

引导学生通过实例分析,理解动态规划的优势。

3. 编程实现:结合教材内容,教授学生如何使用编程语言(如Python)实现0-1背包问题的动态规划解法。

要求学生掌握编程技巧,能够独立完成代码编写。

4. 算法优化与拓展:探讨0-1背包问题的变种及优化方法,如贪心算法、回溯算法等。

指导学生分析各种算法的优缺点,培养学生的算法优化意识。

0-1背包问题——回溯法求解【Python】

0-1背包问题——回溯法求解【Python】

0-1背包问题——回溯法求解【Python】回溯法求解0-1背包问题:问题:背包⼤⼩ w,物品个数 n,每个物品的重量与价值分别对应 w[i] 与 v[i],求放⼊背包中物品的总价值最⼤。

回溯法核⼼:能进则进,进不了则换,换不了则退。

(按照条件深度优先搜索,搜到某⼀步时,发现不是最优或者达不到⽬标,则退⼀步重新选择)注:理论上,回溯法是在⼀棵树上进⾏全局搜索,但是并⾮每种情况都需要全局考虑,毕竟那样效率太低,且通过约束+限界可以减少好多不必要的搜索。

解决本问题思路:使⽤0/1序列表⽰物品的放⼊情况。

将搜索看做⼀棵⼆叉树,⼆叉树的第 i 层代表第 i 个物品,若剩余空间允许物品 i 放⼊背包,扩展左⼦树。

若不可放⼊背包,判断限界条件,若后续继续扩展有可能取得最优价值,则扩展右⼦树(即此 i 物品不放⼊,但是考虑后续的物品)。

在层数达到物品的个数时,停⽌继续扩展,开始回溯。

注:如何回溯呢?怎样得到的,怎样恢复。

放⼊背包中的重量取出,加在bagV上的价值减去。

约束条件:放⼊背包中物品的总质量⼩于等于背包容量限界条件:当前放⼊背包中物品的总价值(i及之前) + i 之后的物品总价值 < 已知的最优值这种情况下就没有必要再进⾏搜索数据结构:⽤⼀个变量记录当前放⼊背包的总价值 bagV(已扩展),⼀个变量记录后续物品的总价值 remainV(未扩展),当前已得到的⼀种最优值 bestV(全局情况),⼀个⽤0/1表⽰的数组bestArr[]记录哪些物品放⼊了背包。

核⼼结构:递归思路进⾏解决。

层层递归,递归到尽头,保留最优值,恢复递归中,层层回溯,即将原来加上去的重量与价值恢复。

# -*- coding:utf-8 -*-def Backtrack(t):global bestV, bagW, bagV,arr, bestArr, cntVif t > n: #某次深度优先搜索完成if bestV < bagV:for i in range(1, n+1):bestArr[i] = arr[i]bestV = bagVelse: #深度优先搜索未完成if bagW + listWV[t][0] <= w: #第t个物品可以放⼊到背包中,扩展左⼦树arr[t] = TruebagW += listWV[t][0]bagV += listWV[t][1]Backtrack(t+1)bagW -= listWV[t][0]bagV -= listWV[t][1]if cntV[t] + bagV > bestV: #有搜索下去的必要arr[t] = FalseBacktrack(t+1)if__name__ == '__main__':w = int(input()) #背包⼤⼩n = int(input()) #物品个数listWV = [[0,0]]listTemp = []sumW = 0sumV = 0for i in range(n):listTemp = list(map(int, input().split())) #借助临时list每次新增物品对应的list加⼊到listWV中sumW += listTemp[0]sumV += listTemp[1]listWV.append(listTemp) #依次输⼊每个物品的重量与价值bestV = 0bagW = 0bagV = 0remainV = sumVarr = [False for i in range(n+1)]bestArr = [False for i in range(n+1)]cntV = [0 for i in range(n+1)] #求得剩余物品的总价值,cnt[i]表⽰i+1~n的总价值 cntV[0] = sumVfor i in range(1, n+1):cntV[i] = cntV[i-1] - listWV[i][1]if sumW <= w:print(sumV)else:Backtrack(1)print(bestV)print(bestArr)print(cntV)检测:1052 65 34 52 43 617[False, True, False, True, False, True][24, 18, 15, 10, 6, 0]。

动态规划之-0-1背包问题及改进

动态规划之-0-1背包问题及改进

动态规划之-0-1背包问题及改进有N件物品和一个容量为V的背包。

第i件物品的重量是w[i],价值是v[i]。

求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

在选择装入背包的物品时,对于每种物品i,只能选择装包或不装包,不能装入多次,也不能部分装入,因此成为0-1背包问题。

形式化描述为:给定n个物品,背包容量C >0,重量第i件物品的重量w[i]>0, 价值v[i] >0 , 1≤i≤n.要求找一n元向量(X1,X2,…,X n,), X i∈{0,1}, 使得∑(w[i] * Xi)≤C,且∑ v[i] * Xi达最大.即一个特殊的整数规划问题。

数学描述为:求解最优值:设最优值m(i,j)为背包容量为j、可选择物品为i,i+1,……,n时的最优值(装入包的最大价值)。

所以原问题的解为m(1,C)将原问题分解为其子结构来求解。

要求原问题的解m(1,C),可从m(n,C),m(n-1,C),m(n-2,C).....来依次求解,即可装包物品分别为(物品n)、(物品n-1,n)、(物品n-2,n-1,n)、……、(物品1,物品2,……物品n-1,物品n)。

最后求出的值即为最优值m(1,C)。

若求m(i,j),此时已经求出m(i+1,j),即第i+1个物品放入和不放入时这二者的最大值。

对于此时背包剩余容量j=0,1,2,3……C,分两种情况:(1)当w[i] > j,即第i个物品重量大于背包容量j时,m(i,j)=m(i+1,j)(2)当w[i] <= j,即第i个物品重量不大于背包容量j时,这时要判断物品i放入和不放入对m的影响。

若不放入物品i,则此时m(i,j)=m(i+1,j)若放入物品i,此时背包剩余容量为 j-w[i],在子结构中已求出当容量k=0,1,2……C 时的最优值m(i+1,k)。

所以此时m(i,j)=m(i+1,j-w[i])+v[i]。

0-1背包问题的枚举算法

0-1背包问题的枚举算法

0-1背包问题的枚举算法一、问题概述0-1背包问题是一种经典的优化问题,给定一组物品,每种物品都有自己的重量和价值,而你有一个限制容量的背包。

目标是在不超过背包容量的情况下,选择物品使得总价值最大化。

然而,在某些情况下,所有的物品都不能被放入背包中,这时就需要用到0-1背包问题的枚举算法。

二、算法原理枚举算法的基本思想是从所有可能的物品组合中逐个尝试,找出满足条件的组合。

对于0-1背包问题,我们可以枚举所有可能的物品组合,对于每个组合,计算其总价值和当前背包的剩余容量,如果总价值大于当前背包容量所能获得的最大价值,那么就将这个物品放入背包中,并更新背包剩余容量和总价值。

如果当前物品的价值小于或等于当前背包容量所能获得的最大价值,那么就将这个物品标记为0(表示已经考虑过),并继续尝试下一个物品。

最终得到的组合就是最优解。

三、算法实现以下是一个简单的Python实现:```pythondefknapsack_enumeration(items,capacity):#初始化结果列表和当前价值result=[]current_value=0#枚举所有可能的物品组合foriinrange(len(items)):#标记当前物品为0(已考虑过)items[i][1]=0#计算当前物品的价值并更新总价值forjinrange(len(items)):ifj<i:#不考虑之前的物品对当前物品的价值影响current_value+=items[j][1]*items[i][0]/capacityelse:#考虑之前的物品对当前物品的价值影响(假设不考虑前一个物品的重量)current_value+=items[j][0]*(capacity-items[i][0])/capacity#将当前物品从物品列表中移除(放入背包中)delitems[i]#将当前价值添加到结果列表中result.append(current_value)returnresult```四、应用场景枚举算法在许多实际应用中都有应用,如计算机科学、运筹学、工程学等。

背包问题:0-1背包、完全背包和多重背包

背包问题:0-1背包、完全背包和多重背包

背包问题:0-1背包、完全背包和多重背包背包问题泛指以下这⼀种问题:给定⼀组有固定价值和固定重量的物品,以及⼀个已知最⼤承重量的背包,求在不超过背包最⼤承重量的前提下,能放进背包⾥⾯的物品的最⼤总价值。

这⼀类问题是典型的使⽤动态规划解决的问题,我们可以把背包问题分成3种不同的⼦问题:0-1背包问题、完全背包和多重背包问题。

下⾯对这三种问题分别进⾏讨论。

1.0-1背包问题0-1背包问题是指每⼀种物品都只有⼀件,可以选择放或者不放。

现在假设有n件物品,背包承重为m。

对于这种问题,我们可以采⽤⼀个⼆维数组去解决:f[i][j],其中i代表加⼊背包的是前i件物品,j表⽰背包的承重,f[i][j]表⽰当前状态下能放进背包⾥⾯的物品的最⼤总价值。

那么,f[n][m]就是我们的最终结果了。

采⽤动态规划,必须要知道初始状态和状态转移⽅程。

初始状态很容易就能知道,那么状态转移⽅程如何求呢?对于⼀件物品,我们有放进或者不放进背包两种选择:(1)假如我们放进背包,f[i][j] = f[i - 1][j - weight[i]] + value[i],这⾥的f[i - 1][j - weight[i]] + value[i]应该这么理解:在没放这件物品之前的状态值加上要放进去这件物品的价值。

⽽对于f[i - 1][j - weight[i]]这部分,i - 1很容易理解,关键是 j - weight[i]这⾥,我们要明⽩:要把这件物品放进背包,就得在背包⾥⾯预留这⼀部分空间。

(2)假如我们不放进背包,f[i][j] = f[i - 1][j],这个很容易理解。

因此,我们的状态转移⽅程就是:f[i][j] = max(f[i][j] = f[i - 1][j] , f[i - 1][j - weight[i]] + value[i])当然,还有⼀种特殊的情况,就是背包放不下当前这⼀件物品,这种情况下f[i][j] = f[i - 1][j]。

0-1背包问题

0-1背包问题

0-1背包问题背包问题:n种物品,每种物品有重量w和价值v,背包所能承受的最⼤重量为c。

如何挑选物品可以使总物品的价值最⼤。

0-1背包问题:限定每种物品的个数只能是0或1.如:5个物品,质量分别为3,5,7,8,9,价值分别为4,6,7,9,10。

背包所能承受重量为22.给物品从0开始编号,挑选物品1,3,4(价值分别为6,9,10)时,总价值最⼤为25,此时重量为22.运⽤动态规划思想,可以构造有效的⽅法。

算法基本思路:1.构造最优表,获取最⼤价值;构造表mv[0...n][0...c],mv[i][j] 表⽰前 i 项物品(即0到i-1)中挑选物品放⼊承受重量为 j 的最⼤价值。

显然,当 i 为0即没有物品时,mv[0][*] = 0;当 j 为0即最⼤重量为0时,mv[*][0] = 0。

(*表⽰任意)对于mv[i][j]只有两种可能的情况,(要注意的是,i 表⽰前i 项物品,物品编号从0开始,即 i-1 表⽰第i 项物品)(1)当w[i-1] > j 即当前物品重量⼤于当前最⼤承受重量时,只能放弃当前物品即 mv[i][j] = mv[i-1][j];(2)当w[i-1] <= j 即当前物品重量⼩于当前最⼤承受重量时,通过⽐较取此物品后的最⼤价值(即mv[i-1][j-w[i-1]] + v[i-1])与不取此物品的最⼤价值(即mv[i-1][j]),挑出较⼤值即 mv[i][j] = max(mv[i-1][j], mv[i-1][j-w[i-1]])。

通过上述⾃底向上打表,可构造出最优值mv[n][c]。

2.通过最优表,获取构造路线即挑出可以得到最⼤价值的物品。

通过上述可知,当w[i-1] > j 时,mv[i][j] = mv[i-1][j];当w[i-1] <= j 时,mv[i][j] = max(mv[i-1][j], mv[i-1][j - w[i-1]] + v[i-1])。

动态规划算法0-1背包问题课件PPT

动态规划算法0-1背包问题课件PPT

回溯法
要点一
总结词
通过递归和剪枝来减少搜索空间,但仍然时间复杂度高。
要点二
详细描述
回溯法是一种基于递归的搜索算法,通过深度优先搜索来 找出所有可能的解。在0-1背包问题中,回溯法会尝试将物 品放入背包中,并递归地考虑下一个物品。如果当前物品 无法放入背包或放入背包的总价值不增加,则剪枝该分支 。回溯法能够避免搜索一些无效的组合,但仍然需要遍历 所有可能的组合,时间复杂度较高。
缺点
需要存储所有子问题的解,因此空间 复杂度较高。对于状态转移方程的确 定和状态空间的填充需要仔细考虑, 否则可能导致错误的结果。
04
0-1背包问题的动态规划解法
状态定义
状态定义
dp[i][ j]表示在前i个物品中选,总 重量不超过j的情况下,能够获得 的最大价值。
状态转移方程
dp[i][ j] = max(dp[i-1][ j], dp[i1][ j-w[i]] + v[i]),其中w[i]和v[i] 分别表示第i个物品的重量和价值。
02
计算时间复杂度:时间复杂度是指求解问题所需的时间与问题规模之间的关系。对 于0-1背包问题,时间复杂度主要取决于状态总数。由于每个状态都需要被遍历, 因此时间复杂度为O(2^n),其中n是物品的数量。
03
空间复杂度:空间复杂度是指求解问题所需的空间与问题规模之间的关系。在0-1 背包问题中,空间复杂度主要取决于状态总数。由于每个状态都需要被存储,因此 空间复杂度也为O(2^n),其中n是物品的数量。
06
0-1背包问题的扩展和实际应用
多多个物品和多个 背包,每个物品有各自的重量和价值, 每个背包有各自的容量,目标是选择物 品,使得在不超过背包容量限制的情况 下,所选物品的总价值最大。

0-1背包问题的近似算法

0-1背包问题的近似算法

0-1背包问题的近似算法0-1背包问题的近似算法对问题特点和算法思想做一些整理如下:这类问题其实很有意思,做数学和做计算机的人都会研究,而且我这里将要提到的论文都是做计算机的人所写的。

问题简述0-1 Knapsack Problem (0-1背包问题,下面简称KP)和Subset Sum Problem (子集合加总问题,下面简称SSP)是经典的NP完全问题。

两个问题简要描述如下:KP:有n个物品要放入背包,第i个物品的价值为ci,占据体积为vi,背包的总容积为V,要选择一部分物品放入背包,使得他们的总价值最大。

对应的优化问题是maxxi∑ci∗xis.t.∑vi∗xi≤V,xi∈{0,1}这里xi代表是否选取第i个物品进背包,等于1就代表放入背包,等于0代表不放入背包。

SSP: 给一个集合{c1,c2,…,cn},还有一个目标值V,问能否选出一个子集,使得子集中元素求和刚好等于V。

我们一般考虑的是他的另一种表述方式:选出一个子集,使得子集中元素求和不超过V,且尽量大。

对应的优化问题是maxxi∑ci∗xis.t.∑ci∗xi≤V,xi∈{0,1}这里xi代表是否选入子集,等于1就是选入子集,等于0就是不选入子集。

SSP是KP的特殊情况,也即当ci=vi的时候,KP退化为SSP,从问题式子上看,也完全一样了。

尽管如此,研究了KP不代表就不用研究SSP了,后面会说明这一点。

精确算法与近似算法这两个问题都有很简单的动态规划算法可以精确求解,但可惜算法的时间复杂度是伪多项式的,也即和V相关,但V不是问题输入数据的规模,n才是。

在ACM竞赛等算法比赛中,经常会遇到一些问题属于KP的变种,而伪多项式算法也就足够了。

由于网上资料很多,而且难度不大,这里就不详细介绍了。

如果你不知道,请你搜索“动态规划求解0-1背包问题”。

这里我们更关心多项式近似算法,也即PTAS(Polynomial Time Approximation Scheme),也即对任意给定的ϵ,算法可以在关于n的多项式时间内求得一个解,且该解和真实最优解的最多相差ϵ倍。

0-1背包问题(回溯法)

0-1背包问题(回溯法)

0-1背包问题(回溯法)实验报告姓名:学号:指导老师:一.算法设计名称:0-1背包问题(回溯法)二.实验内容问题描述:给定n 种物品和一背包。

物品i 的重量是w i ,其价值为v i ,背包的容量为C 。

问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i 只有两种选择,即装入背包或不装入背包。

不能将物品装入背包多次,也不能只装入部分的物品。

三.实验目的1.运用回溯思想,设计解决上述问题的算法,找出最大背包价值的装法。

2.掌握回溯法的应用四.算法设计:问题求解思路1.由0-1背包问题的最优子结构性质,建立计算m[i][j]的递归式如下:i i i w j w j j i m i v w j i m j i m j i m <≤≥⎩⎨⎧-+---=0],1[]}[],1[],,1[max{),(2.查找装入背包物品的回溯函数:从0-1二叉树的根开始搜索:若是叶子节点,则判断此时的价值是否比当前最优的价值大,否则将之替换,并获得最优解向量且返回;若不是叶子节点,则向左右子树搜索,先改变当前的数据状态,递归的调用自己,然后恢复数据状态表示回溯。

3.边界函数bound主要是当还未搜索到叶子节点时,提前判断其子树是否存可能存在更优的解空间,否则进行回溯,即裁剪掉子树的解空间。

关键数据结构及函数模块:(Backtrack.h )#ifndef __BACKTRACK_H__#define __BACKTRACK_H__class BP_01_P{public:∑=ni i i x v 1max ⎪⎩⎪⎨⎧≤≤∈≤∑=n i x C x w i n i i i 1},1,0{1BP_01_P(int w,int n):m_Sum_weitht(0),m_Number(0) {m_Sum_weitht=w;m_Number=n;bestHav=0;bestVal=0;curVal=0;curHav=0;m_hav=new int[n];m_val=new int[n];temop=new int[n];option=new int[n];}~BP_01_P(){delete []m_hav;delete []m_val;delete []temop;delete []option;}void traceBack(int n);int bound(int n);void printBestSoulation();int *m_hav;//每个物品的重量int *m_val;//每个物品的价值int *temop;//01临时解int *option;//01最终解int bestHav;//最优价值时的最大重量int bestVal;//最优的价值int curVal;//当前的价值int curHav;//当前的重量private:int m_Sum_weitht;//背包的总容量int m_Number;//物品的种类};#endif __BACKTRACK_H__五:主要的算法代码实现:(Backtrack.cpp)边界函数:bound( )int BP_01_P::bound(int n){int hav_left=m_Sum_weitht-curHav;int bo=curVal;while(n<m_Number && m_hav[n]<=hav_left){hav_left-=m_hav[n];bo+=m_val[n];n++;}if(n<m_Number){bo+=m_val[n]*hav_left/m_hav[n];//bo+=hav_left;}return bo;}回溯递归函数:traceBack( )void BP_01_P::traceBack(int n){if(n>=m_Number){if(curVal>=bestVal){bestVal=curVal;for(int i=0;i<n;i++){option[i]=temop[i];}return ;}}if(curHav+m_hav[n]<=m_Sum_weitht)//向左子树搜索 {curHav=curHav+m_hav[n];curVal=curVal+m_val[n];temop[n]=1;//标记要选择这个物品traceBack(n+1);curHav=curHav-m_hav[n];curVal=curVal-m_val[n];}if(bound(n+1)>bestVal)//向右子树搜索{temop[n]=0;//标记要丢弃这个物品traceBack(n+1);}}主控函数:(main.cpp)#include <iostream>#include "Backtrack.h"using namespace std;int main(){int number,weigth;cout<<"包的总容量:";cin>>weigth;cout<<"物品的种类:";cin>>number;BP_01_P *ptr=new BP_01_P(weigth,number);cout<<"各种物品的重量:"<<endl;for(int i=0;i<number;i++)cin>>ptr->m_hav[i];cout<<"各种物品的价值:"<<endl;for(i=0;i<number;i++)cin>>ptr->m_val[i];ptr->traceBack(0);ptr->printBestSoulation();cout<<"总重量:"<<ptr->bestHav<<"\t总价值:"<<ptr->bestVal<<endl;return 0;}六:算法分析采用回溯法解决0-1背包问题,明显比动态规划法更优良。

0-1背包问题的递归方法

0-1背包问题的递归方法

0-1背包问题的递归方法0-1背包问题是一个经典的动态规划问题,可以使用递归方法求解。

定义一个函数`knapsack(weights, values, capacity, n)`,其中`weights`和`values`分别代表物品的重量和价值,`capacity`代表背包的容量,`n`代表当前考虑的物品个数。

递归的思路是对于每个物品,有两种选择:放入背包中或者不放入背包中。

1. 如果第`n`个物品的重量大于背包的容量`capacity`,则不放入背包中,返回`0`;2. 否则,有两种选择:- 选择放入第`n`个物品,则总价值为第`n`个物品的价值加上考虑前`n-1`个物品,背包容量减去第`n`个物品重量的最优解; - 不放入第`n`个物品,则总价值为考虑前`n-1`个物品,背包容量不变的最优解。

代码如下所示:```pythondef knapsack(weights, values, capacity, n):if n == 0 or capacity == 0:return 0if weights[n-1] > capacity:return knapsack(weights, values, capacity, n-1)else:return max(values[n-1] + knapsack(weights, values, capacity-weights[n-1], n-1),knapsack(weights, values, capacity, n-1))```可以通过调用`knapsack`函数来求解0-1背包问题,如下所示:```pythonweights = [2, 3, 4, 5]values = [3, 4, 5, 6]capacity = 5n = len(weights)result = knapsack(weights, values, capacity, n)print(result)```以上代码会输出最优解的总价值。

背包问题问题实验报告(3篇)

背包问题问题实验报告(3篇)

第1篇一、实验目的1. 理解背包问题的基本概念和分类。

2. 掌握不同背包问题的解决算法,如0-1背包问题、完全背包问题、多重背包问题等。

3. 分析背包问题的复杂度,比较不同算法的效率。

4. 通过实验验证算法的正确性和实用性。

二、实验环境1. 操作系统:Windows 102. 编程语言:Python3.73. 开发工具:PyCharm4. 实验数据:随机生成的背包物品数据三、实验内容1. 0-1背包问题(1)问题描述:给定n个物品,每个物品的重量为w[i],价值为v[i],背包的容量为C。

求将哪些物品装入背包,使得背包内物品的总价值最大。

(2)解决算法:动态规划法(3)实验步骤:a. 初始化一个二维数组dp[n+1][C+1],其中dp[i][j]表示前i个物品在容量为j 的背包中的最大价值。

b. 遍历每个物品,对于每个容量,根据物品的重量和价值计算dp值。

c. 返回dp[n][C],即为最大价值。

2. 完全背包问题(1)问题描述:给定n个物品,每个物品的重量为w[i],价值为v[i],背包的容量为C。

求将哪些物品装入背包,使得背包内物品的总价值最大,且每个物品可以重复取。

(2)解决算法:动态规划法(3)实验步骤:a. 初始化一个一维数组dp[C+1],其中dp[j]表示容量为j的背包的最大价值。

b. 遍历每个物品,对于每个容量,根据物品的重量和价值更新dp值。

c. 返回dp[C],即为最大价值。

3. 多重背包问题(1)问题描述:给定n个物品,每个物品的重量为w[i],价值为v[i],背包的容量为C。

每个物品有无限个,求将哪些物品装入背包,使得背包内物品的总价值最大。

(2)解决算法:动态规划法(3)实验步骤:a. 初始化一个一维数组dp[C+1],其中dp[j]表示容量为j的背包的最大价值。

b. 遍历每个物品,对于每个容量,根据物品的重量和价值更新dp值。

c. 返回dp[C],即为最大价值。

四、实验结果与分析1. 0-1背包问题实验结果显示,在背包容量为100时,最大价值为298。

0-1背包问题(分支限界法)

0-1背包问题(分支限界法)

分支限界法——01背包问题12软工028 胡梦颖一、问题描述0-1背包问题:给定n种物品和一个背包。

物品i的重量是Wi,其价值为Vi,背包的容量为C。

应如何选择装入背包的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。

不能将物品i装入背包多次,也不能只装入部分的物品i。

二、问题分析分支限界法类似于回溯法,也是在问题的解空间上搜索问题解的算法。

一般情况下,分支限界法与回溯法的求解目标不同。

回溯法的求解目标是找出解空间中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

由于求解目标不同,导致分支限界法与回溯法对解空间的搜索方式也不相同。

回溯法以深度优先的方式搜索解空间,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间。

分支限界法的搜索策略是,在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一扩展结点。

为了有效地选择下一扩展结点,加速搜索的进程,在每一个活结点处,计算一个函数值(限界),并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。

这种方式称为分支限界法。

人们已经用分支限界法解决了大量离散最优化的问题。

三.源代码#include <stdio.h>#include<malloc.h>#define MaxSize 100 //结点数的最大值typedef struct QNode{float weight;float value;int ceng;struct QNode *parent;bool leftChild;}QNode,*qnode;typedef struct{qnode Q[MaxSize];int front,rear;}SqQueue; //存放结点的队列SqQueue sq;float bestv=0; //最优解int n=0; //实际物品数float w[MaxSize]; //物品的重量float v[MaxSize]; //物品的价值int bestx[MaxSize]; // 存放最优解qnode bestE;void InitQueue(SqQueue &sq ) //队列初始化{sq.front=1;sq.rear=1;}bool QueueEmpty(SqQueue sq) //队列是否为空{if(sq.front==sq.rear)return true;elsereturn false;}void EnQueue(SqQueue &sq,qnode b) //入队{if(sq.front==(sq.rear+1)%MaxSize){printf("队列已满!");return;}sq.Q[sq.rear]=b;sq.rear=(sq.rear+1)%MaxSize;} qnode DeQueue(SqQueue &sq) //出队{qnode e;if(sq.front==sq.rear){printf("队列已空!");return 0;}e=sq.Q[sq.front];sq.front=(sq.front+1)%MaxSize;return e;}void EnQueue1(float wt,float vt, int i ,QNode *parent, bool leftchild) {qnode b;if (i==n) //可行叶子结点{ if (vt==bestv){ bestE=parent;bestx[n]=(leftchild)?1:0;}return;}b=(qnode)malloc(sizeof(QNode)); //非叶子结点b->weight=wt;b->value=vt;b->ceng=i;b->parent=parent;b->leftChild=leftchild;EnQueue(sq,b);}void maxLoading(float w[],float v[],int c){float wt=0;float vt=0;int i=1; //当前的扩展结点所在的层float ew=0; //扩展节点所相应的当前载重量float ev=0; //扩展结点所相应的价值qnode e=NULL;qnode t=NULL;InitQueue(sq);EnQueue(sq,t); //空标志进队列while (!QueueEmpty(sq)){wt=ew+w[i];vt=ev+v[i];if (wt <= c){if(vt>bestv)bestv=vt;EnQueue1(wt,vt,i,e,true); // 左儿子结点进队列} EnQueue1(ew,ev,i,e,false); //右儿子总是可行;e=DeQueue(sq); // 取下一扩展结点if (e == NULL){if (QueueEmpty(sq))break;EnQueue(sq,NULL); // 同层结点尾部标志e=DeQueue(sq); // 取下一扩展结点i++;}ew=e->weight; //更新当前扩展结点的值ev=e->value;}printf("最优取法为:\n");for( int j=n-1;j>0;j--) //构造最优解{bestx[j]=(bestE->leftChild?1:0);bestE=bestE->parent;}for(int k=1;k<=n;k++){if(bestx[k]==1)printf("物品%d:重量:%.1f,价值:%.1f\n",k,w[k],v[k]);}printf("最大价值为:%.1f\n",bestv);}void main(){int c;float ewv[MaxSize];printf("请输入背包的最大容量v:");scanf("%d",&c);printf("请输入物品总数n:");scanf("%d",&n);printf("请输入物品的重量和单位重量价值:\n");for(int i=1;i<=n;i++){printf("第%d件物品:",i);scanf("%f%f",&w[i],&ewv[i]);v[i]=w[i]*ewv[i];}maxLoading(w,v,c);}五.实验结果。

0-1背包问题的学习及LeetCode相关习题练习

0-1背包问题的学习及LeetCode相关习题练习

0-1背包问题的学习及LeetCode相关习题练习0-1背包问题:n件物品,它们装⼊背包所占的容量分别为w1、w2……w n;它们所拥有的价值分别为v1、v2 ……v n;有⼀个总容量为C的背包;在装满背包的情况下,如何使得包内的总价值最⼤?该问题的特点是:每个物品仅有⼀个,可以选择放或者不放,也就是说每个物品只能使⽤⼀次。

思路:1.⾸先定义⼀个状态转移数组dp,dp[i][j]表⽰前i件物品放⼊容量为j的背包中所能得到的最⼤价值;2.寻找数组元素之间的关系式,也就是状态转移⽅程,我们将第i件物品是否放⼊背包中这个⼦问题拿出来进⾏分析,⾸先要明确的是我们的⼀切⽬标都是使得在既有的背包容量下,能够得到最⼤的价值,所以对于第i件物品,如果放⼊能够使得在现有的容量下背包价值最⼤,则dp[i][j] = dp[i-1][j-w] + v;如果不能,则就不把第i件物品放⼊背包,那么在dp[i][j] = dp[i-1][j],即前i件物品放⼊容量为j的背包中所得到的最⼤价值就是前i-1件物品放⼊容量为j的背包中所得的的最⼤价值。

总结⼀下,状态转移⽅程就是: dp[i][j] = max{dp[i][j] = dp[i-1][j-w] + v,dp[i][j] = dp[i-1][j]}3.确定初始值,dp[0][0]表⽰前0件物品放⼊容量为0的背包中的最⼤价值,那么就是0,⽽对于多有i=0和j=0的元素,其值都是0;模拟过程:举⼀个例⼦来模拟程序整个的执⾏过程;i123w123v6913现在有三件物品,这些物品的价值和所占容量如上表所⽰,有⼀个容量为5的背包,在装满背包的情况下,如何使得背包⾥的价值最⼤?通过⼀个表格来显⽰状态转移数组的内部情况:i\j0123450000000106666620691515153069151922该表格表⽰dp数组内部的元素值,程序的执⾏过程如下: i = 1 ==> w = 1,v = 6 :1dp[1][1] = max(dp[0][1],dp[0][0]+6)2dp[1][2] = max(dp[0][2],dp[0][1]+6)3dp[1][3] = max(dp[0][3],dp[0][2]+6)4dp[1][4] = max(dp[0][4],dp[0][3]+6)5dp[1][5] = max(dp[0][5],dp[0][4]+6) i = 2 ==> w = 2,v=9: ………… 以此类推,可以得到状态转移表格中的数据。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

课程设计说明书设计题目: 0-1背包问题的动态规划算法设计专业:班级:设计人:山东科技大学2013年12月5日课程设计任务书学院:专业:班级:姓名:一、课程设计题目:二、课程设计主要参考资料(1)计算机算法设计与分析(第3版)王晓东著(2)三、课程设计应解决的主要问题(1) 0-1背包问题的动态规划算法设计(2)(3)四、课程设计相关附件(如:图纸、软件等):(1)(2)五、任务发出日期: 2013-11-21 课程设计完成日期: 2013-12-5 指导教师签字:系主任签字:指导教师对课程设计的评语成绩:指导教师签字:年月日0-1背包问题的实现一、设计目的1.运用动态规划思想,设计解决上述问题的算法,找出最大背包价值的装法。

2.掌握动态规划的应用。

二、设计要求给定n种物品和一背包。

物品i的重量是wi,其价值为vi,背包的容量为C。

问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。

不能将物品装入背包多次,也不能只装入部分的物品。

0-1背包问题是一个特殊的整数规划问题。

三、设计说明(一)、需求分析1、问题描述:形式化描述:给定c >0, w i>0, v i>0 , 1≤i≤n.要求找一n元向量(x1,x2,…,xn,), xi∈{0,1}, ∋∑w i x i≤c,且∑v i x i达最大.即一个特殊的整数规划问题。

2、最优子结构性质:设(y1,y2,…,y n)是(3.4.1)的一个最优解.则(y2,y3,…,y n)是下面相应子问题的一个最优解:证明:使用反证法。

若不然,设(z2,z3,…,z n)是上述子问题的一个最优解,而(y2,y3,…,y n)不是它的最优解。

显然有∑v i z i> ∑v i y i(i=2,…,n)且w1y1+ ∑w i z i<= c因此v 1 y 1+ ∑v i z i (i=2,…,n) > ∑v i y i , (i=1,…,n)说明(y 1,z 2,z 3,…,z n )是(3.4.1)0-1背包问题的一个更优解,导出(y 1,y 2,…,y n )不是背包问题的最优解,此为矛盾。

所以0-1背包问题具有最有子结构。

(二)、概要设计1、递推关系:设所给0-1背包问题的子问题的最优值为m(i ,j),即m(i ,j)是背包容量为j ,可选择物品为i ,i+1,…,n 时0-1背包问题的最优值。

由0-1背包问题的最优子结构性质,可以建立计算m(i ,j)的递归式:注:(3.4.3)式此时背包容量为j ,可选择物品为i 。

此时在对xi 作出决策之后,问题处于两种状态之一:(1)背包剩余容量是j,没产生任何效益;(2)剩余容量j-w i ,效益值增长了v i ;2、设计思路(1).由0-1背包问题的最优子结构性质,建立计算m[i][j]的递归式如下:i i i i w j w j j i m v w j i m j i m j i m <≤≥⎩⎨⎧-+---=0),1(}),1(),,1(max{),( n n nw j w j vj m <≤≥⎩⎨⎧=00),n ((2).查找装入背包物品的函数:从数组的最右下角开始寻找,如若m[i][c] != m[i-1][c],则该第i 个物品就在背包中,将其从最大价值量中去掉,然后再接着寻找下一个在背包中的物品,直至i=0。

关键数据结构:一个二维数组,两个一维数组,两个整型变量 int n;//n 表示物品的种类double c;//表示背包重量的最大值double w[N],v[N];//分别表示物品的重量和价值double **p;//由于存放跳跃点函数模块:Type Knapsack(int n,Type c,Type v[],Type w[],Type **p,int x[]);//整理背包函数,找出最大价值void Traceback(int n,Type w[],Type v[],Type **p,int *head,int x[]);//找出所有在背包里的物品的函数3、算法的改进:由m(i,j)的递归式容易证明,在一般情况下,对每一个确定的i(1≤i≤n),函数m(i,j)是关于变量j的阶梯状单调不减函数。

跳跃点是这一类函数的描述特征。

在一般情况下,函数m(i,j)由其全部跳跃点唯一确定。

如图所示。

对每一个确定的i(1≤i≤n),用一个表p[i]存储函数m(i,j)的全部跳跃点。

表p[i]可依计算m(i,j)的递归式递归地由表p[i+1]计算,初始时p[n+1]={(0,0)}。

函数m(i,j)是由函数m(i+1,j)与函数m(i+1,j- w i)+ v i作max运算得到的。

因此,函数m(i,j)的全部跳跃点包含于函数m(i+1,j)的跳跃点集p[i+1]与函数m(i+1,j- w i)+ v i的跳跃点集q[i+1]的并集中。

易知,(s,t)∈q[i+1]当且仅当wi<=s<=c且(s-wi,t-vi)∈p[i+1]。

因此,容易由p[i+1]确定跳跃点集q[i+1]如下:q[i+1]=p[i+1]⊕(w i,v i)={(j+wi,m(i,j)+vi)|(j,m(i,j))∈p[i+1]}另一方面,设(a,b)和(c,d)是p[i+1]∪q[i+1]中的2个跳跃点,则当c>=a且d<b时,(c,d)受控于(a,b),从而(c,d)不是p[i]中的跳跃点。

除受控跳跃点外,p[i+1]∪q[i+1]中的其他跳跃点均为p[i]中的跳跃点。

由此可见,在递归地由表p[i+1]计算表p[i]时,可先由p[i+1]计算出q[i+1],然后合并表p[i+1]和表q[i+1],并清除其中的受控跳跃点得到表p[i]。

例:n=5,c=10,w={2,2,6,5,4},v={6,3,5,4,6}。

跳跃点的计算过程如下:初始时p[6]={(0,0)},(w5,v5)=(4,6)。

因此,q[6]=p[6]⊕(w5,v5)={(4,6)}。

p[5]={(0,0),(4,6)}。

q[5]=p[5]⊕(w4,v4)={(5,4),(9,10)}。

从跳跃点集p[5]与q[5]的并集p[5]∪q[5]={(0,0),(4,6),(5,4),(9,10)}中看到跳跃点(5,4)受控于跳跃点(4,6)。

将受控跳跃点(5,4)清除后,得到p[4]={(0,0),(4,6),(9,10)}q[4]=p[4]⊕(6,5)={(6,5),(10,11)}p[3]={(0,0),(4,6),(9,10),(10,11)}q[3]=p[3]⊕(2,3)={(2,3),(6,9)}p[2]={(0,0),(2,3),(4,6),(6,9),(9,10),(10,11)}q[2]=p[2]⊕(2,6)={(2,6),(4,9),(6,12),(8,15)}p[1]={(0,0),(2,6),(4,9),(6,12),(8,15)}p[1]的最后的那个跳跃点(8,15)给出所求的最优值为m(1,c)=15。

(三)、详细设计#include <iostream>using namespace std;template<class Type>Type Knapsack(int n,Type c,Type v[],Type w[],Type **p,int x[]){int *head = new int[n+2];head[n+1]=0;p[0][0]=0;//p[][0]存储物品重量p[0][1]=0;//p[][1]存储物品价值,物品n的跳跃点(0,0)// left 指向p[i+1]的第一个跳跃点,right指向最后一个//拿书上的例子来说,若计算p[3]=0;则left指向p[4]的第一跳跃点(0 0)right指向(9,10)int left = 0,right = 0,next = 1;//next即下一个跳跃点要存放的位置head[n]=1;//head[n]用来指向第n个物品第一个跳跃点的位置for(int i=n; i>=1; i--){int k = left;//k指向p[ ]中跳跃点,移动k来判断p[]与p[]+(w v)中的受控点for(int j=left; j<=right; j++){if(p[j][0]+w[i]>c) break;//剩余的空间不能再装入i,退出for循环;Type y = p[j][0] + w[i],m = p[j][1] + v[i];//计算p[ ]+(w v)//若p[k][0]较小则(p[k][0] p[k][1])一定不是受控点,将其作为p[i]的跳跃点存储while(k<=right && p[k][0]<y){p[next][0]=p[k][0];p[next++][1]=p[k++][1];}//如果 p[k][0]==y而m<p[k][1],则(y m)为受控点不存if(k<=right && p[k][0]==y){if(m<p[k][1])//对(p[k][0] p[k][1])进行判断{m=p[k][1];}k++;}// 若p[k][0]>=y且m> =p[k][1],判断是不是当前i的最后一个跳跃点的受控点//若不是则为i的跳跃点存储if(m>p[next-1][1]){p[next][0]=y;p[next++][1]=m;}//若是,则对下一个元素进行判断。

while(k<=right && p[k][1]<=p[next-1][1]){k++;}}while(k<=right){p[next][0]=p[k][0];p[next++][1]=p[k++][1];//将i+1剩下的跳跃点作为做为i 的跳跃点存储}left = right + 1;right = next - 1;// 第i-1个物品第一个跳跃点的位置 head[0]指第0个物品第一个跳跃点的位置head[i-1] = next;}Traceback(n,w,v,p,head,x);return p[next-1][1];}//x[]数组存储对应物品0-1向量,0不装入背包,1表示装入背包template<class Type>void Traceback(int n,Type w[],Type v[],Type **p,int *head,int x[]){//初始化j,m为最后一个跳跃点对应的第0列及第1列//如上例求出的最后一个跳跃点为(8 15)j=8,m=15Type j = p[head[0]-1][0],m=p[head[0]-1][1];for(int i=1; i<=n; i++){x[i]=0;// 初始化数组;for(int k=head[i+1]; k<=head[i]-1;k++)// 初始k指向p[2]的第一个跳跃点(0 0){//判断物品i是否装入,如上例与跳跃点(6 9)相加等于(8 15)所以1装入if(p[k][0]+w[i]==j && p[k][1]+v[i]==m){x[i]=1;//物品i被装入,则x[i]置1j=p[k][0];// j和m值置为满足if条件的跳跃点对应的值m=p[k][1];// 如上例j=6,m=9break;//再接着判断下一个物品}}}}int main(){double c;int n;cout << "请输入物品个数: ";cin >> n ;cout << endl << "请输入背包的承重:";cin >> c;double *w = new double[n+1];double *v = new double[n+1];int *x = new int[n+1];cout << endl << "请输入每个物品的重量 (w[i])和价值 (v[i]): " << endl;for(int i = 1; i <= n; i++)cin >> w[i] >> v[i];for(int i = 1;i <= n;i++)x[i]=0;double **p = new double *[50];for(int i=0;i<50;i++){p[i] = new double[2];}cout<<"背包能装的最大价值为:"<<Knapsack(n,c,v,w,p,x)<<endl;cout<<"背包装下的物品编号为:"<<endl;for(int i=1; i<=n; i++){if(x[i]==1){cout<<i<<" ";}}cout<<endl;for(int i=0;i<50;i++){delete p[i];}delete[] p;return 0;}四、运行结果及分析分析:上述算法的主要计算量在于计算跳跃点集p[i](1≤i ≤n)。

相关文档
最新文档