01背包较好理解的讲解
0-1背包问题详解二(完全背包问题)

0-1背包问题详解⼆(完全背包问题)问题描述给定n种物品和⼀个背包。
物品i的重量是w(i),其价值为v(i),背包的容量为c(即最多能够装c重量的物品)。
这n种物品可以重复放置(这是与普通背包问题的不同之处)。
输⼊n=5,c=6.物品容量和价值分别为:2 62 36 55 44 6最后输出时:18问题求解:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}似乎利⽤上⾯那个公式就可以很好的求出解。
这⾥给出另外⼀组公式,该公式和上⽂的公式是⼀样的,只是第⼆个for语句的倒过来。
for i=1..Nfor v=0..Vf[v]=max{f[v],f[v-cost]+weight}为什么这样⼀改就可⾏呢?⾸先想想为什么上⽂中要按照v=V..0的逆序来循环。
这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推⽽来。
换句话说,这正是为了保证每件物品只选⼀次,保证在考虑“选⼊第i件物品”这件策略时,依据的是⼀个绝⽆已经选⼊第i件物品的⼦结果f[i-1][v-c[i]]。
⽽现在完全背包的特点恰是每种物品可选⽆限件,所以在考虑“加选⼀件第i种物品”这种策略时,却正需要⼀个可能已选⼊第i种物品的⼦结果f[i][vc[i]],所以就可以并且必须采⽤v=0..V的顺序循环。
这就是这个简单的程序为何成⽴的道理。
1void compelteKnapsack(){2int c,n;3 cout<<"请输⼊最⼤容量,⼩于100"<<endl;4 cin>>c;5 cout<<"请输⼊背包个数"<<endl;6 cin>>n;7 cout<<"请输⼊各个背包重量和价值"<<endl;8for(int i=1;i<=n;i++){9 cin>>w[i]>>v[i];10 }11for(int i=0;i<=n;i++)12 p[i]=0;13for(int i=1;i<=n;i++)14for(int j=w[i];j<=c;j++)15 p[j]=max(p[j],p[j-w[i]]+v[i]);16 cout<<"结果是"<<p[c]<<endl;17 }View Code版权所有,欢迎转载,但是转载请注明出处:。
背包问题(1)

背包问题报告小组成员:张灿、吴雪涛、高坤、占强、习慧平小组分工情况小组成员查找资料制作ppt 编写程序讲解ppt 制作报告张灿ⅴⅴⅴⅴⅴ吴雪涛ⅴ高坤ⅴⅴ占强ⅴ习慧平ⅴ背包问题一、背包问题的历史由来它是在1978年由Merkel和Hellman提出的。
它的主要思路是假定某人拥有大量物品,重量各不同。
此人通过秘密地选择一部分物品并将它们放到背包中来加密消息。
背包中的物品中重量是公开的,所有可能的物品也是公开的,但背包中的物品是保密的。
附加一定的限制条件,给出重量,而要列出可能的物品,在计算上是不可实现的。
背包问题是熟知的不可计算问题,背包体制以其加密,解密速度快而其人注目。
在解决大量的复杂组合优化问题时,它常常作为一个子问题出现,从实际的观点看,许多问题可以用背包问题来描述,如装箱问题,货仓装载,预算控制,存储分配,项目选择决策等,都是典型的应用例子。
随着网络技术的不断发展,背包公钥密码在电子商务中的公钥设计中也起着重要的作用。
然而当问题的规模较大时,得到最优解是极其困难的。
但是,大多数一次背包体制均被破译了,因此现在很少有人使用它。
二、背包问题的描述背包问题(Knapsack problem)是一种组合优化的NP完全问题。
问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。
问题的名称来源于如何选择最合适的物品放置于给定背包中。
相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。
也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?三、背包问题的定义我们有n种物品,物品j的重量为w j,价格为p j。
我们假定所有物品的重量和价格都是非负的。
背包所能承受的最大重量为W。
如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题。
可以用公式表示为:maximizesubject to如果限定物品j最多只能选择b j个,则问题称为有界背包问题。
优先队列式分支限界法求解0-1背包问题

算法分析与设计实验报告第7 次实验}1、测试自己输入的小规模数据2、测试随机生成1003、随机生成1000数据4、随机生成1000数据附录:完整代码#include <iostream>#include<time.h>#include<algorithm>#include<fstream>using namespace std;ifstream in("input.txt");ofstream out("output.txt");typedef int Typew;typedef int Typep;//物品类class Object{friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); public:int operator <= (Object a) const{return (d >= a.d);}private:int ID; //物品编号float d; //单位重量价值};//树结点类class bbnode{friend class Knap;friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); private:bbnode *parent; //指向父节点的指针int LChild;};//堆结点类class HeapNode{friend class Knap;friend class MaxHeap;public:operator Typep()const{return uprofit;};private:Typep uprofit, //结点的价值上界profit; //结点所相应的价值Typew weight; //结点所相应的重量int level; //活结点在子集树中所处的层序号bbnode *elemPtr; //指向该活结点在子集树中相应结点的指针};//最大堆类class MaxHeap{public:MaxHeap(int maxElem){HeapElem = new HeapNode* [maxElem+1]; //下标为0的保留capacity = maxElem;size = 0;}void InsertMax(HeapNode *newNode);HeapNode DeleteMax(HeapNode* &N);private:int capacity;int size;HeapNode **HeapElem;};//0-1背包问题的主类class Knap{friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); public:Typep MaxKnapsack();private:MaxHeap *H;Typep Bound(int i);void AddLiveNode(Typep up, Typep cp, Typew cw, int ch, int level);bbnode *E; //指向扩展结点的指针Typew c; //背包容量int n; //物品总数Typew *w; //物品重量数组(以单位重量价值降序)Typep *p; //物品价值数组(以单位重量价值降序)Typew cw; //当前装包重量Typep cp; //当前装包价值int *bestx; //最优解};void MaxHeap::InsertMax(HeapNode *newNode){int i = 1;for (i = ++size; i/2 > 0 && HeapElem[i/2]->uprofit < newNode->uprofit; i /= 2){HeapElem[i] = HeapElem[i/2];}HeapElem[i] = newNode;}HeapNode MaxHeap::DeleteMax(HeapNode *&N){if(size >0 ){N = HeapElem[1];int i = 1;while(i < size){if(((i*2 +1) <= size) && HeapElem[i*2]->uprofit > HeapElem[i*2 +1]->uprofit){HeapElem[i] = HeapElem[i*2];i = i*2;}else{if(i*2 <= size){HeapElem[i] = HeapElem[i*2];i = i*2;}elsebreak;}}if(i < size)HeapElem[i] = HeapElem[size];}size--;return *N;}Typep Knap::MaxKnapsack(){H = new MaxHeap(10000);bestx = new int [n+1];int i = 1;E = 0;cw = 0;cp = 0;Typep bestp = 0;Typep up = Bound(1);while (i != n+1){Typew wt = cw + w[i];if(wt <= c) {if(cp + p[i] > bestp)bestp = cp + p[i];AddLiveNode(up, cp + p[i], cw + w[i], 1, i);}up = Bound(i + 1);if(up >= bestp)AddLiveNode(up, cp, cw, 0, i);HeapNode* N;H->DeleteMax(N);E = N->elemPtr;cw = N->weight;cp = N->profit;up = N->uprofit;i = N->level + 1;}for (int i = n; i > 0; i--){bestx[i] = E->LChild;E = E->parent;}return cp;}Typep Knap::Bound(int i){Typew cleft = c - cw;Typep b = cp;while (i<=n && w[i] <= cleft){cleft -= w[i];b += p[i];i++;}if(i<=n) b += p[i]/w[i] * cleft;return b;}void Knap::AddLiveNode(Typep up, Typep cp, Typew cw, int ch, int level) {bbnode *b=new bbnode;b->parent=E;b->LChild=ch;HeapNode *N = new HeapNode;N->uprofit=up;N->profit=cp;N->weight=cw;N->level=level;N->elemPtr=b;H->InsertMax(N);}//Knapsack返回最大价值,最优值保存在bestxTypep Knapsack(Typew *w, Typep *p, Typew c, int n, int *bestx){Typew W = 0;Typep P = 0;Object *Q = new Object[n];for(int i =1; i<=n; i++){Q[i-1].ID = i;Q[i-1].d = 1.0*p[i]/w[i];P += p[i];W += w[i];}if (W <= c){for(int i =1; i<=n; i++){bestx[i] = p[i];}return P;}for(int i = 1; i<n; i++)for(int j = 1; j<= n-i; j++){if(Q[j-1].d < Q[j].d){Object temp = Q[j-1];Q[j-1] = Q[j];Q[j] = temp;}}Knap K;K.p = new Typep [n+1];K.w = new Typew [n+1];for(int i = 1; i<=n; i++){K.p[i] = p[Q[i-1].ID];K.w[i] = w[Q[i-1].ID];}K.cp = 0;K.cw = 0;K.c = c;K.n = n;Typep bestp = K.MaxKnapsack();for(int i = 1; i<=n; i++){bestx[Q[i-1].ID] = K.bestx[i];}delete [] Q;delete [] K.w;delete [] K.p;delete [] K.bestx;delete [] K.H;return bestp;}int main(){cout<<"请在input.txt文件中输入物品数量、背包容量"<<endl;int N ;in>>N;Typew c; //背包容量in>>c;int bestx[N+1]; //最优解int bestp; //最优值Typep p[N+1];//物品价值Typew w[N+1];//物品重量cout<<"在input.txt文件中读取的物品总数N = "<< N<<",背包容量C = "<< c<<endl; cout<<"请选择生成数据的规模大小:200请输入1,2000请输入2,20000请输入3"<<endl; int x;cin>>x;if(x==1){ofstream in1("input1.txt");srand(time(NULL));int n=200;int *a=new int[n];for(int i=0;i<n;i++){a[i]=rand()%91;in1<<a[i]<<" ";}cout<<"随机数已请生成到input1文件中,请将数据添加到input.txt文件中"<<endl; }else if(x==2){ofstream in1("input1.txt");srand(time(NULL));int n=2000;int *a=new int[n];for(int i=0;i<n;i++){a[i]=rand()%91;in1<<a[i]<<" ";}cout<<"随机数已请生成到input1文件中,请将数据添加到input.txt文件中"<<endl; }else if(x==3){ofstream in1("input1.txt");srand(time(NULL));int n=20000;int *a=new int[n];for(int i=0;i<n;i++){a[i]=rand()%91;in1<<a[i]<<" ";}cout<<"随机数已请生成到input1文件中,请将数据添加到input.txt文件中"<<endl;}cout<<"添加完毕后请输入1"<<endl;int m;cin>>m;clock_t start,finish;start=clock();for (int i = 1; i <= N; i++){in>>w[i];}for (int i = 1; i <= N; i++){in>>p[i];}cout<<"已在input文件中读取物品重量和价值。
背包较好理解的讲解

文档从网络中收集,已重新整理排版.word版本可编辑.欢迎下载支持.动态规划之01背包问题(最易理解的讲解)01背包问题,是用来介绍动态规划算法最经典的例子,网上关于01背包问题的讲解也很多,我写这篇文章力争做到用最简单的方式,最少的公式把01背包问题讲解透彻。
01 背包的状态转换方程f[i,j] = Max{ f[i-l,j-Wi]+Pi( j >= Wi), f[i-l,j]}f[i,j]表示在前i件物品中选择若干件放在承重为j的背包中,可以取得的最大价值。
Pi表示第i件物品的价值。
决策:为了背包中物品总价值最大化,第i件物品应该放入背包中吗?题目描述:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,54它们的价值分别是6,3,5,4,6, 现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?name weight value12345678910 a26066991212151515 b23033669991011 c65000666661011 d54000666661010 e460006666666只要你能通过找规律手工填写出上而这张表就算理解了01背包的动态规划算法。
首先要明确这张表是至底向上,从左到右生成的。
为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e 时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重虽是4,背包装不了。
对于d2单元格,表示只有物品e, d时,承重为2的背包,所能装入的最大价值,仍然是0, 因为物品e,d都不是这个背包能装的。
同理,c2=0, b2=3,a2=6c对于承重为8的背包,a8=15,是怎么得出的呢?根据01背包的状态转换方程,需要考察两个值,一个是对于这个例子来说就是b8的值9,另一个是f[i-l,j-Wi]+Pi;在这里,表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值f[i-lj-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值f[i-l,j-Wi]就是指单元格b6,值为9, Pi指的是a物品的价值,即6由于f[i-l,j-Wi]+Pi = 9 + 6 = 15大于f[i-l,j] = 9,所以物品a应该放入承重为8的背包以I'是actionscript3的代码[java] view plaincopy在CODE上査看代码片派生到我的代码片public function getOlPackageAnswer(bagltGms:AfTay,bagSize:int):Array{var bagMatrix:Array=[];var i:int;var item:Packageltem;for(i=0;i<bagltems」en gth;i++){bagMatrix[i] = [0];}for(i=l;i<=bagSize;i++){for(var j:int=O;j<bagltems.le ngth;j++){item = bagltemsfj] as Packageitem;if(item.weight > i){//i背包转不下itemif(j=O){bagMatrix[j][i] = 0;}else{bagMatrix[j][i]=bagMatrix[j-l][i];}}else{〃将item装入背包后的价值总和var itemlnBag:int;if(j=O){bagMatrix[j][i] = item.value;contin ue;}else{itemlnBag = bagMatrix[j-l][i-item.weight]+item.value;}bagMatrix[j][i] = (bagMatrix[j-l][i] > itemlnBag ? bagMatrix[j-l][i]: itemlnBag) }}}//find answervar answers:Array=[];var curSize:i nt = bagSize;for(i=bagltems」gngth・l;i>=O;i—){item = bagltems[i] as Packageitem;if(curSize==O){break;}if(i==0 && curSize > 0){an swers.push();break;}if(bagMatrix[i][curSize]-bagMatrix[i-l][curSize-item.weight]==item.value){an swers.push();curSize -= item.weight;}}return answers;}Packageitem 类[java] view plaincopy在CODE上査看代码片派生到我的代码片public class Packageitempublic var name:String;public var weight:int;public var value:int;public function PackageltemfnameiString^eightnn^valueHnt){ = name;this.weight = weight;this.value = value;}}测试代码[java] view plaincopy在CODE上査看代码片派生到我的代码片var nameArr:Array=[l a7b,/,c,/,d,/e,];var weightArr:Array=[2/2,6,5,4];var valueArr:Array=[6/3/5/4/6);var bagltems:Array=[);for(var i:int=O;i<nameArr・length;i++){var bagltem:Packageltem = new PackageltemfnameArrfil^eightArrl^valueArrli]);bagltems[i]=bagltem;}var arr:Array = ac.getOlPackageAnswerfbagltems.lO);。
背包问题九讲

背包问题九讲2.0RC1崔添翼(Tianyi Cui)*2011-09-28†本文题为《背包问题九讲》,从属于《动态规划的思考艺术》系列。
这系列文章的第一版于2007年下半年使用EmacsMuse制作,以HTML格式发布到网上,转载众多,有一定影响力。
2011年9月,本系列文章由原作者用L A T E X重新制作并全面修订,您现在看到的是2.0alpha版本,修订历史及最新版本请访问https:///tianyicui/pack查阅。
本文版权归原作者所有,采用CC BY-NC-SA协议发布。
Contents101背包问题31.1题目 (3)1.2基本思路 (3)1.3优化空间复杂度 (3)1.4初始化的细节问题 (4)1.5一个常数优化 (4)1.6小结 (5)2完全背包问题52.1题目 (5)2.2基本思路 (5)2.3一个简单有效的优化 (5)2.4转化为01背包问题求解 (6)2.5O(V N)的算法 (6)2.6小结 (7)3多重背包问题73.1题目 (7)3.2基本算法 (7)3.3转化为01背包问题 (7)3.4可行性问题O(V N)的算法 (8)*a.k.a.dd_engi†Build2011092818380013.5小结 (9)4混合三种背包问题94.1问题 (9)4.201背包与完全背包的混合 (9)4.3再加上多重背包 (9)4.4小结 (10)5二维费用的背包问题105.1问题 (10)5.2算法 (10)5.3物品总个数的限制 (10)5.4二维整数域N2上的背包问题 (11)5.5小结 (11)6分组的背包问题116.1问题 (11)6.2算法 (11)6.3小结 (12)7有依赖的背包问题127.1简化的问题 (12)7.2算法 (12)7.3较一般的问题 (12)7.4小结 (13)8泛化物品138.1定义 (13)8.2泛化物品的和 (13)8.3背包问题的泛化物品 (14)8.4小结 (14)9背包问题问法的变化149.1输出方案 (15)9.2输出字典序最小的最优方案 (15)9.3求方案总数 (15)9.4最优方案的总数 (16)9.5求次优解、第K优解 (16)9.6小结 (17)2101背包问题1.1题目有N件物品和一个容量为V的背包。
01背包方案数

01背包⽅案数简单的01背包题⽬⼤意:有n个物品和m的容量,求所有区间lr内物品的拿取种类和,结果对998244353取模。
n,s,ai都是3000 。
思路:拿01背包算种类复杂度是ns。
再去枚举所有的区间,复杂度是n2*ns。
void solve(){cin >> n >> m ;rep(i , 1 , n) cin >> a[i] ;ll ans = 0 ;rep(L , 1 , n)rep(s , 1 , n - L + 1){vector<vector<ll> > dp(n + 1 , vector<ll>(m + 1)) ;dp[s - 1][0] = 1 ;//initrep(i , s , s + L - 1)rep(j , 0 , m){if(j < a[i]) dp[i][j] = dp[i - 1][j] ;else dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - a[i]]) % mod ;}ans = (ans + dp[s + L - 1][m] ) % mod ;}cout << ans ;}然后我们发现在做1->n的背包时实际上已经把1->i的种类都算过了,就是说我们没有必要再去枚举右区间,只要枚举左区间就⾏。
这样的时间复杂度就变成了n2*s 。
void solve(){cin >> n >> m ;rep(i , 1 , n) cin >> a[i] ;ll ans = 0 ;rep(s , 1 , n){vct<vct<ll> > dp(n + 1 , vct<ll> (m + 1)) ;dp[s - 1][0] = 1 ;rep(i , s , n){rep(j , 0 , m){if(j < a[i])dp[i][j] = dp[i - 1][j] ;else dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - a[i]]) % mod ;}ans = (ans + dp[i][m]) % mod ;}}cout << ans << "\n" ;}继续优化,将普通01背包的状态表⽰更改成以i结尾的所有区间种数和。
01背包问题的数学逻辑

01背包问题的数学逻辑1.引言1.1 概述01背包问题是一类经典的组合优化问题,它是数学逻辑中的一个重要问题之一。
在实际生活中,我们经常会面对资源有限的情况,而如何在有限的资源下做出最佳决策,已经成为一个重要的研究领域。
01背包问题就是在给定总容量和一组物品的情况下,选取其中的一些物品放入背包中,使得背包中物品的总价值最大化,而不超过背包的总容量。
这个问题由G. Dantzig在1957年首次提出,并且成为组合优化中的一个经典问题。
它的名字来源于背包只能放入0或1个同样特性的物品。
虽然问题看似简单,但由于问题的解空间庞大,是一个NP完全问题,因此求解过程通常使用一些近似算法。
1.2 目的本文的目的是探究01背包问题的数学逻辑,并介绍一些常用的求解方法。
通过深入研究01背包问题,我们可以更好地理解其数学模型,在实际应用中解决类似的优化问题。
具体目标包括:1. 分析01背包问题的数学模型,并介绍相关的定义和术语;2. 探讨01背包问题的求解方法,包括动态规划、贪心算法和近似算法等;3. 介绍优化问题的评价指标,包括背包的总价值、总重量和可行性等;4. 分析不同情况下的算法复杂性,讨论解决01背包问题的时间和空间复杂性;5. 举例说明01背包问题在实际生活中的应用,如旅行行李、采购决策等。
通过对01背包问题的研究,我们能够更好地理解和应用数学逻辑,提高问题求解的能力。
了解背包问题的求解方法和评价指标,对我们在实际生活中面对资源有限的情况下做出最佳决策具有重要意义。
无论是在物流管理、金融投资还是其他领域,都可以通过对01背包问题的研究,提高决策的效率和准确度。
在接下来的文章中,将会详细介绍01背包问题的数学逻辑,分析不同求解方法的优劣,并给出实际应用的例子,以便读者更好地理解和应用该问题。
2.正文2.1 01背包问题的定义和背景介绍01背包问题是运筹学中的一个经典问题,在算法和动态规划中有重要的应用。
该问题的核心是在给定的背包容量和一组物品的情况下,如何选择物品放入背包中,使得背包中的物品总价值最大化。
背包问题之零一背包

背包问题之零⼀背包注:参考⽂献《背包九讲》.零⼀背包问题⼀:题⽬描述 有 N 件物品和⼀个容量为 V 的背包.放⼊第 i 件物品耗⽤的费⽤为C i(即所占⽤背包的体积),得到的价值是 W i.求将哪些物品装⼊背包所得到的总价值最⼤.⼆:基本思路 01背包是最基础的背包问题,这道题的特点是每种物品仅有⼀件,可以选择放或不放,且不要求背包必须被放满,只要求最后的总价值最⼤. ⽤⼦问题定义状态:F[i][v] 表⽰对于前 i 件物品,当背包容量为 v 时所能得到的价值最⼤值.设想,将 "前 i 件物品放⼊容量为 v 的背包中" 这个⼦问题,若只考虑第 i 件物品的策略(要么放要么不放),那么就可以转化为⼀个之和前 i - 1 件物品相关的问题.如果不放第 i 件物品, 那么问题就转化为 ”前 i - 1 件物品放⼊容量为 v 的背包中“,价值就是 F[i - 1][v]; 如果放第 i 件物品,那么问题就转化为 ”前 i - 1 件物品放⼊剩下的容量为v - C i的背包中”, 此时获得的价值为 F[i - 1][v - C i] + W i。
特殊的,当 v < C i时,可以认为当前的容量是放不下第 i 件物品的,即此时相当于不放第 i 件物品的价值F[i - 1][v].分析到这⾥则可得状态转移⽅程为: F[i][v] = v < C i F[i - 1][v] : max( F[i - 1][v], F[i - 1][v - C i] + W i ).在这⾥要特别的说明⼀下,这个⽅程⾮常重要,⼀定要知道这是怎么推出来的,⼏乎后⾯的所有的背包问题都和这个⽅程有着密不可分的联系.伪代码如下:F[0...N][0...V] <--- 0for i <--- 1 to N for v <--- C i to V F[i][v] = v < C i F[i - 1][v] : max( F[i - 1][v], F[i - 1][v - C i] + W i );具体代码:1void _01Pack(int F[][MAXV], int N, int V, int C[], int W[]){2 memset(F, 0, sizeof(F));3for(int i = 1; i <= N; i++) {4for(int v = 0; v <= V; v++) {5 F[i][v] = v < C[i] ? F[i - 1][v] : max(F[i - 1][v], F[i - 1][v - C[i]] + W[i]); //放或者不放两者之中选择最优者6 }7 }8 }三:优化空间复杂度 可以清楚的看到上⾯算法的时间复杂度和空间复杂度均为 O(N * V), 这⾥时间复杂度已经不能得到优化,但是空间复杂度确可以优化到O(V). 先看上⾯代码是如何实现的.最外⾯⼀层循环,每次计算出⼆维数组 F[i][0...V] 的值,计算的时候 F[i][0...V] 是由它的上⼀层 F[i - 1][0...V] ⽽得到的.那么如果把这个数组换成⼀维的 F[v] 那么还能保留上⼀次的状态吗.答案是可以的.由于动态规划算法的⽆后效性,第 i + 1 件物品的选择与否不会影响到第 i 件物品(即它的前⼀件物品)的选择状态.那么可以在上⾯第⼆次循环中按照 v <--- V...0 递减的顺序来计算 F[v], 这样计算F[v] 时所需要的状态 F[v] 和 F[v - C i] + W i 仍然还是上⼀次的状态.⽽计算 F[v] 之后, v 的顺序是递减的, F[v] 不会影响到 F[v'] (v' < v), 因为F[v']只与 F[v'](上⼀次的值) 和 F[v - C i] 有关, ⽽ F[v] > F[v'] > F[v' - C i]. 所以⼜可得状态转移⽅程. F[v] = max( F[v], F[v - C i] + W i ).伪代码如下:F[0...V] <--- 0for i <--- 1 to N for v <--- V to C i F[v] = max( F[v], F[v - C i] + W i );具体代码:1void _01Pack(int F[], int N, int V, int C[], int W[]){2 memset(F, 0, sizeof(F));3for(int i = 1; i <= N; i++) {4for(int v = V; v >= C[i]; v--) {5 F[i][v] = max(F[v], F[v - C[i]] + W[i]);6 }7 }8 }可以看到从第⼀个状态转移⽅程到第⼆个状态转移⽅程的空间优化效率还是挺⼤的: F[i][v] = max( F[i - 1][v], F[i - 1][v - C i] + W i ). ----> F[v] = max( F[v], F[v - C i] + W i ).在第⼆个⽅程中 F[v]1 = max(F[v]2, F[v - C i] + W i), 其实 F[v]2 就相当与⽅程⼀中的 F[i - 1][v], 对应的 F[v - C i] + W i就相当于 F[i -1][v - C i] + W i.这⼀正确性是在内层循环递减的前提下才成⽴的.否则, 将内层循环改为递增, 那么 F[i][v] 其实是由 F[i][v] 和 F[i][v - C i] 推出来的,这不符合基本思路中的探讨.之前说过由于 01背包的特殊性,这⾥将 01背包抽象化,⽅便之后的调⽤.解决单个物品 01背包的伪代码:def ZeroOnePack (F, C, W) for v <--- V to C F[v] = max( F[v], F[v - C] + W );这么写之后, 01背包总问题解决的伪代码就可以改写成:F[0...V] <--- 0for i <--- 1 to N ZeroOnePack(F, C[i], W[i]);具体代码:1const int MAXN = 10000;2int N, V, C[MAXN], W[MAXN];34void ZeroOnePack(int F[], int C, int W) { // 对于单个物品的决策5for(int v = V; v >= C; v--) {6 F[v] = max(F[v], F[v- C] + W);7 }8 }910void solv(int F[]) {11 memset(F, 0, sizeof(F));12for(int i = 1; i <= V; i++) {13 ZeroOnePack(F, C[i], W[i]);14 }15 }四: 01背包问题的拓展 ------ 初始化的细节问题 在上述 01背包的问题中,仅问得是 “如何选取,才能使的最后的总价值最⼤”, 这⾥并没有规定是否必须装满背包, 但是有的题将会给予这个附加条件, 即“在要求恰好装满背包的前提下, 如何选取物品, 才能使的最后的总价值最⼤ ”. 这两种问法, 在代码实现上相差⽆⼏.如果是上述问法,要求 “恰好装满背包”, 那么在初始化时除了将 F[0] 赋值为 0 之外, 其他的 F[1...V] 都应该赋值为 -∞,这样就可以保证最后的得到的 F[V] 是⼀种恰好装满背包的最优解.如果没有要求必须把背包装满,⽽是只希望价值尽量最⼤,初始化时应该将F[0...V] 全部设置为 0. 之所以可以这么做,是因为初始化的 F[] 事实就是没有任何物品放⼊背包时的合法状态.如果要求背包恰好装满,那么只有容量为 0 的背包在什么也不装且价值为 0 的情况下被装 "恰好装满",其他容量的背包如果不装物品, 那么默认的情况下都是不合法状态,应该被赋值为 -∞, 即对于第⼀个物品⽽⾔, 其合法状态只能由 F[0] 转移得到.如果背包并⾮必须被装满,那么任何容量的背包在没有物品可装时都存在⼀个合法解,即什么都不装,且这个解的价值为 0.所以将其全部初始化为 0 是可以的. 注:这个技巧完全可以拓展到其他背包问题中.伪代码:def ZeroOnePack (F, C, W) for v <--- V to C F[v] = max( F[v], F[v - C] + W )end defdef slov() F[0] = 0, F[1...V] <--- -∞ for i <--- 1 to N ZeroOnePack(F, C[i], W[i])end def具体代码:1const int MAXN = 10000;2int N, V, C[MAXN], W[MAXN];34void ZeroOnePack(int F[], int C, int W) {5for(int v = V; v >= C; v--) {6 F[v] = max(F[v], F[v- C] + W);7 }8 }910void solv(int F[]) {11 F[0] = 0;12for(int i = 1; i <= V; i++) F[i] = INT_MIN; // 除F[0] = 0之外, 其他全部赋值为负⽆穷13for(int i = 1; i <= V; i++) {14 ZeroOnePack(F, C[i], W[i]);15 }16 }五:⼀个常数级别的优化上述伪代码的:for i <--- 1 to N for v <--- V to C i可以优化为:for i <--- 1 to N for v <--- V to max( V - SUM(i...N)C i, C i)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
动态规划之01背包问题(最易理解的讲解)
分类:01背包动态规划2012-07-06 17:09 31770人阅读评论(13) 收藏举报
算法c
01背包问题,是用来介绍动态规划算法最经典的例子,网上关于01背包问题的讲解也很多,我写这篇文章力争做到用最简单的方式,最少的公式把01背包问题讲解透彻。
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的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
只要你能通过找规律手工填写出上面这张表就算理解了01背包的动态规划算法。
首先要明确这张表是至底向上,从左到右生成的。
为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。
对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。
同理,c2=0,b2=3,a2=6。
对于承重为8的背包,a8=15,是怎么得出的呢?
根据01背包的状态转换方程,需要考察两个值,
一个是f[i-1,j],对于这个例子来说就是b8的值9,另一个是f[i-1,j-Wi]+Pi;
在这里,
f[i-1,j]表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值
f[i-1,j-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值
f[i-1,j-Wi]就是指单元格b6,值为9,Pi指的是a物品的价值,即6
由于f[i-1,j-Wi]+Pi = 9 + 6 = 15 大于f[i-1,j] = 9,所以物品a应该放入承重为8的背包
以下是actionscript3 的代码
[java] view plaincopy在CODE上查看代码片派生到我的代码片
public function get01PackageAnswer(bagItems:Array,bagSize:int):Array
{
varbagMatrix:Array=[];
var i:int;
varitem:PackageItem;
for(i=0;i<bagItems.length;i++)
{
bagMatrix[i] = [0];
}
for(i=1;i<=bagSize;i++)
{
for(var j:int=0;j<bagItems.length;j++)
{
item = bagItems[j] as PackageItem;
if(item.weight>i)
{
//i背包转不下item
if(j==0)
{
bagMatrix[j][i] = 0;
}
else
{
bagMatrix[j][i]=bagMatrix[j-1][i];
}
}
else
{
//将item装入背包后的价值总和
varitemInBag:int;
if(j==0)
{
bagMatrix[j][i] = item.value;
continue;
}
else
{
itemInBag = bagMatrix[j-1][i-item.weight]+item.value;
}
bagMatrix[j][i] = (bagMatrix[j-1][i] >itemInBag ? bagMatrix[j-1][i] : itemInBag)
}
}
}
//find answer
varanswers:Array=[];
varcurSize:int = bagSize;
for(i=bagItems.length-1;i>=0;i--)
{
item = bagItems[i] as PackageItem;
if(curSize==0)
{
break;
}
if(i==0 &&curSize> 0)
{
answers.push();
break;
}
if(bagMatrix[i][curSize]-bagMatrix[i-1][curSize-item.weight]==item.value) {
answers.push();
curSize -= item.weight;
}
}
return answers;
}
PackageItem类
[java] view plaincopy在CODE上查看代码片派生到我的代码片
public class PackageItem
{
publicvarname:String;
publicvarweight:int;
publicvarvalue:int;
public function PackageItem(name:String,weight:int,value:int)
{
= name;
this.weight = weight;
this.value = value;
}
}
测试代码
[java] view plaincopy在CODE上查看代码片派生到我的代码片
varnameArr:Array=['a','b','c','d','e'];
varweightArr:Array=[2,2,6,5,4];
varvalueArr:Array=[6,3,5,4,6];
varbagItems:Array=[];
for(var i:int=0;i<nameArr.length;i++)
{
varbagItem:PackageItem = new PackageItem(nameArr[i],weightArr[i],valueArr[i]); bagItems[i]=bagItem;
}
vararr:Array = ac.get01PackageAnswer(bagItems,10);。