分支限界法:
分支限界法(branch and bound method)是求解纯整数规划或混合整数规划问题的经典方法,在上世纪六十年代由Land Doig和Dakin等人提出。这种方法灵活且便于用计算机求解,目前已经成功运用于求解生产进度问题、旅行推销员问题、工厂选址问题、背包问题及分配问题等。算法基本思想如下:
- 以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树
- 分支限界法中,每一个活结点只有一次机会成为扩展结点,活结点一旦成为扩展结点,就一次性产生其所有儿子结点,其中导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中
- 然后从活结点表中取下一结点成为当前扩展结点
- 重复上述结点扩展过程,直至到找到所需的解或活结点表为空时为止
问题表述:
假定n个商品重量分别为w0, w1, ..., wn-1,价值分别为p0, p1, ..., pn-1,背包载重量为C。怎样选择商品组合,使得价值最高?
队列式分支限界法
按照队列先进先出(FIFO)原则选取下一个结点为扩展结点,从活结点表中取出结点的顺序与加入结点的顺序相同,因此活结点表的性质与队列相同
#include <iostream>
#include <queue>
using namespace std;
int maxV = -99999,maxCount = -1;
int main(void)
{
int num = 0,bag = 0;
int *feet[3],count = 0;
int *weight,*value;
queue<int> things,values,bags; //广度遍历时保存状态
cout<<"请输入背包的容量"<<endl;
cin>>bag;
cout<<"请输入物品的数目"<<endl;
cin>>num;
num++;
weight = (int *)malloc(sizeof(int)*num);
value = (int *)malloc(sizeof(int)*num);
feet[0] = (int *)malloc(sizeof(int)*num);
feet[1] = (int *)malloc(sizeof(int)*num);
feet[2] = (int *)malloc(sizeof(int)*num);
cout<<"请输入物品的属性:重量和价值"<<endl;
for(int i=1;i<num;i++)
{
cin>>weight[i]>>value[i];
}
for(int i=1;i<num;i++)
{
things.push(i);
values.push(0);
bags.push(bag);
while(!things.empty())
{
int curNum = things.front();
int curValue = values.front();
int curBag = bags.front();
curBag -= weight[curNum];
curValue += value[curNum];
//记录足迹
feet[0][count] = curNum;
feet[1][count] = values.front();
feet[2][count++] = curValue;
things.pop();
values.pop();
bags.pop();
//子节点满足条件的依次加入队列
for(int j = curNum+1;j<num;j++)
{
if(curBag-weight[j]>=0)
{
things.push(j);
values.push(curValue);
bags.push(curBag);
}
}
if(curValue>maxV)
{
maxV = curValue;
maxCount = count-1;
}
}
}
cout<<"最大价值为:"<<maxV<<endl;
int valueS = feet[1][maxCount];
cout<<"最优解个数为:"<<feet[0][maxCount]<<" ";
for(int i = maxCount;i>=0;i--)
{
if(valueS==feet[2][i])
{
cout<<feet[0][i]<<" ";
valueS = feet[1][i];
}
}
cout<<endl;
return 0;
}
优先队列式分支限界法(代价最小或效益最大):
每个结点都有一个对应的耗费或收益,以此决定结点的优先级,从优先队列中选取优先级最高的结点成为当前扩展结点,如果查找一个具有最小耗费的解:则活结点表可用小顶堆来建立,下一个扩展结点就是具有最小耗费的活结点 ,如果希望搜索一个具有最大收益的解:则可用大顶堆来构造活结点表,下一个扩展结点是具有最大收益的活结点。
#include <iostream>
#include <stack>
using namespace std;
typedef struct node
{
int maxValue;
int bag;
int deep;
int route[20]; //记录路径
}node;
int search(int *weight,int *value,int bag,int num);
node maxNode;
int main(void)
{
int num = 0,bag = 0;
int *weight,*value;
cout<<"请输入背包的容量"<<endl;
cin>>bag;
cout<<"请输入物品的数目"<<endl;
cin>>num;
num++;
weight = (int *)malloc(sizeof(int)*num);
value = (int *)malloc(sizeof(int)*num);
cout<<"请输入物品的属性:重量和价值"<<endl;
for(int i=1;i<num;i++)
{
cin>>weight[i]>>value[i];
}
cout<<search(weight,value,bag,num)<<endl;
for(int i=0;i<num-1;i++)
{
cout<<maxNode.route[i]<<" ";
}
cout<<endl;
return 0;
}
int search(int *weight,int *value,int bag,int num)
{
int max = -1;
stack<node> list;
node start;
start.maxValue = 0;
start.bag = bag;
start.deep = 0;
list.push(start);
while(!list.empty())
{
node curNode = list.top();
list.pop();
int curBag = curNode.bag;
int curDeep = curNode.deep;
int curMaxValue = curNode.maxValue;
if(curDeep>=num-1) continue;
node nodeTmpLeft,nodeTmpRight = curNode;
nodeTmpRight.deep = curDeep + 1;
for(int i=0;i<=curDeep;i++)
{
nodeTmpLeft.route[i] = curNode.route[i];
nodeTmpRight.route[i] = curNode.route[i];
}
nodeTmpRight.route[curDeep] = 0;
if(curBag>=weight[curDeep+1])
{
nodeTmpLeft.maxValue = curMaxValue + value[curDeep+1];
nodeTmpLeft.deep = curDeep + 1;
nodeTmpLeft.bag = curBag - weight[curDeep+1];
nodeTmpLeft.route[curDeep] = 1;
if(max<nodeTmpLeft.maxValue)
{
max = nodeTmpLeft.maxValue;
maxNode = nodeTmpLeft;
}
if(nodeTmpLeft.maxValue>curMaxValue)
{
list.push(nodeTmpRight);
list.push(nodeTmpLeft);
}else
{
list.push(nodeTmpLeft);
list.push(nodeTmpRight);
}
}else
{
list.push(nodeTmpRight);
}
}
return max;
}