问题:
给定 n 中物品和一个背包。物品i的重量是Wi,其价值位Vi ,背包的容量为C。问应该如何选择装入背包的物品,使得放入背包的物品的总价值为最大?
这是经典的动态规划例题,但也可以用解空间树下的回溯法求解,下面以样例输入为例:
输入:
物品个数n=4, 背包容量c=7, 物品价值:p={9,10,7, 4},物品重量:w={3,5,2,1}
以满二叉树的结构来解决问题,
物品个数为4,则树的高度为5(这里只以高度为4为例),没次往左走的枝代表1,表示此物品放入,往右走的枝代表0,表示此物品不放入。
设cw为当前重量,cp为当前价值,bestp为当前最优价值, k为下一个要访问的物品
明显,把所有的路径走一遍,在满足cw+w[k]<=c(不会超重)的前提下,记录每次的价值,找最大即可,
但这样会遍历2的n次方,时间复杂度大,因此需要剪枝,即判定有些情况是否需要继续遍历,
在遍历左子树时,表示此物品放入,只需判断cw+w[k]<=c(不会超重)即可,
而在遍历右子树时,表示此物品不放入,需要计算此右子树中所有解得上界(最大值),记为bound,若bound<bestp,即右子树里所有解的上界也比遍历前面找到的当前最优价值小,则不需要进入此右子树,剪枝舍掉即可。
需要理解的是,上界只是理论值,并不一定是一种合理的解,需要通过每个物品的单位价值(价值/重量)计算,有时此物品无法放入,只要放入部分使价值最大即可。因此,在遍历整棵树前,可以按照单位价值递减的顺序遍历,因此会先对物品按照单位价值递减排序。
计算上界的函数为:
#include <bits/stdc++.h> using namespace std; int counts;//物品数量 int m;//背包 int cw=0;//当前重量 int cp=0;//当前价值 int bestp=0;//当前最优价值 typedef struct Node { double pValue;//物品价值 double pWeight;//物品重量 }Goods; Goods goods[1000]; //计算在前k-1件物品已经做出决策的前提下,可能达到的最大的效益值,返回一个上界, double Bound(int k)//k代表是考虑第几个物品, { double bound=(double) cp; int a=cw; while(k<=counts) { a+=goods[k].pWeight; if(a<=m) bound+=goods[k].pValue; else //此时只填入部分第k个物品,使背包满 { double yu=(double)(m-cw)*(double)(goods[k].pValue/goods[k].pWeight); return bound+yu; } ++k; } return bound; } bool cmp(Goods a,Goods b) { return (a.pValue/a.pWeight)>(b.pValue/b.pWeight); } void BackKnap(int i) { if(i>counts)//到达叶节点 { bestp=cp; return; } if(cw+goods[i].pWeight<=m)//进入左子树 { cw+=goods[i].pWeight; cp+=goods[i].pValue; BackKnap(i+1); cw-=goods[i].pWeight; cp-=goods[i].pValue; } if(Bound(i+1)>bestp) BackKnap(i+1); } int main() { cout<<"请输入物品个数以及背包容量"<<endl; cin>>counts>>m; goods[0].pValue=0; cout<<"请输入物品的重量"<<endl; for(int i = 1; i <= counts; i++) { cin>>goods[i].pWeight; } cout<<"请输入物品的价值"<<endl; for(int i = 1; i <= counts; i++) { cin>>goods[i].pValue; } sort(goods,goods+counts+1,cmp); BackKnap(1); cout<<"最大价值为"<<endl; cout << bestp<< endl; return 0; }