算法思想
分支限界算法类似于回溯算法,是在问题的解空间树上搜索问题的解的算法,主要体现在两点不同:
1,求解的目标不同。回溯算法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界的求解目标是找出满足约束条件的一个解,或者是在满足约束条件的解中找出某种意义上的最优解
2,搜索解空间树的方式不同。回溯算法以深度优先搜索解空间树,而分支限界是以广度优先搜索(以最小耗费优先)
分支限界法长以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。在分支限界法中每一个活结点只有一次机会称为扩展节点,活结点一旦成为扩展节点,就一次性产生其所有的儿子节点(分支),在这些儿子节点中,导致不可行解或是导致非最优解的儿子节点会被舍弃掉,其余儿子节点会被加入活结点列表中。
为了有效的选择下一个扩展节点加速搜索,在每一个活结点处计算一个函数值(限界),并根据计算的函数值结果从当前活结点表中取下一个最有利的节点成为当前的扩展节点,是搜索朝着解空间树上最优解的分支推进。重复上述节点扩展过程,直到找到所需的最优解或者活结点表为空
扩展节点:一个正在产生儿子的节点称作扩展节点
活结点:一个自身已经产生但其儿子还没有生成
死节点:一个所有儿子已经产生的节点
深度优先搜索是对一个扩展节点R,一旦产生了一个儿子C,就把C当做新的扩展节点。在完成对子树C的深度优先搜索之后回溯到R时,将重新变成扩展节点,继续生成R的下一个儿子
广度优先搜索是在一个扩展节点R变成死节点之前,它一直都是扩展节点
从活结点表中选择下一个扩展节点时,不同的方式会导致不同的分支限界法:
1,普通队列式
a,一开始根节点是唯一的活结点,根节点加入活结点队列
b,从活结点队列中取出对头节点后,作为当前扩展节点
c,对当前扩展节点,先从左到右产生它的所有孩子节点,用约束条件检查,把所有满足约束函数的孩子节点加入活结点队列中
d,再从活结点表中取出队首节点为当前扩展节点,重复上述过程,直到找到一个解或活结点队列为空为止。
2,优先级队列分支限界法
a,对每一个活结点计算一个优先级(某些信息的函数值)
b,根据这些优先级从当前活结点表中优先选择一个优先级最高(最有利)的节点作为扩展节点,是搜索朝着解空间树上有最优解的分支推进,以便尽快找出一个最优解
c,对当前扩展节点,先从左到右产生它的所有孩子节点,用约束条件检查,对所有满足约束函数的孩子节点计算优先级并加入到活结点优先级队列中。
d,再从活节点表中取出下一个优先级最高的节点为当前扩展节点,重复上述过程,直到找到一个解或者队列为空为止 。
集装箱装载问题


cpp
int w[] = { 12,8,15 };//集装箱货物的重量
const int len = sizeof(w) / sizeof(w[0]);
int c = 20;//轮船的容量
int cw = 0;//已选择物品的重量
int bestw = 0;//记录最优的装载量
//分支限界算法
int r = 0;//表示未选择的重量
//先创建节点
struct Node
{
Node(int w, int i, Node* parent, bool isleft)
{
this->w = w;
this->i = i;
this->parent = parent;
this->isleft = isleft;
}
int w;//当前节点的重量
int i;//是为了记录子节点
//这两个主要是为了方便找出那个节点被选为最优解
Node* parent;
bool isleft;
};
queue <Node*> que;
Node* bestNode = NULL;
void addNode(int w, int i, Node* parent, bool isleft)
{
Node* newnode = new Node(w, i, parent, isleft);
que.push(newnode);
if (i == len && w == bestw)
{
bestNode = newnode;
}
}
int maxR(int level)
{
int j=0;
for (int i = level; i < len; i++)
{
j += w[i];
}
return j;
}
int main()
{
Node* node = nullptr;
int i = 0;
while (i < len)
{
//选择i节点物品
int wt = cw + w[i];
if (wt <= c)
{
if (wt > bestw)
{
bestw = wt;
}
//将该节点的字节点放入队列中,左孩子
addNode(wt, i + 1, node, true);
}
//再将右孩子放入队列中
r = maxR(i+1);
if(cw+r>=bestw)
addNode(cw, i + 1, node, false);
//开始出队列去更新i和cw
node = que.front();
que.pop();
i = node->i;
cw = node->w;
}
cout << bestw<<endl;
int dex[3] = { 0 };
for (int j = 2; j >= 0; j--)
{
dex[j] = bestNode->isleft == true ? 1 : 0;
bestNode = bestNode->parent;
}
for (int v : dex)
{
cout << v << " ";
}
return 0;
}
背包问题(优先级队列)

cpp
int w[] = { 16,15,15 };//物品的重量
int v[] = { 45,25,25 };//物品的价值
int c = 30;//背包的容量
struct Node
{
Node(int w, int v, int i, Node* parent, bool isleft,int upbound)
{
this->i = i;
this->isleft = isleft;
this->parent = parent;
this->v = v;
this->w = w;
this->upbound = upbound;
}
int w;
int v;
int i;
Node* parent;//用来记录父节点,方便回溯打印最优解
bool isleft;//用来记录是否向左走(是否被选择)
int upbound;//其实就是upbound越大,越先出队列
};
#include<functional>
int bestv = 0;
int len = sizeof(w) / sizeof(w[0]);
int cw = 0;
int cv = 0;
class MyCompare
{
public:
bool operator()(Node* n1,Node*n2)
{
return n1->upbound < n2->upbound;
}
};
//priority_queue<Node*, vector<Node*>, function<bool(Node*, Node*)>>que([](Node* n1, //Node* n2)->bool {return n1->upbound < n2->upbound; });
priority_queue<Node*,vector<Node*>,MyCompare>que;
void addNode(int w, int v, int i, Node* node, bool isleft,int upbound)
{
Node* newnode = new Node(w, v, i, node, isleft, upbound);
que.push(newnode);
}
int maxBound(int i)
{
int n = cv;
for (int j = i; j < len; j++)
{
n += v[j];
}
return n;
}
int main()
{
int i = 0;
Node* node = nullptr;
int upbound = maxBound(0);
while (i < len)
{
int wt = cw + w[i];
if (wt <= c)
{
if (cv + v[i] > bestv)
{
bestv = cv + v[i];
}
addNode(wt, cv + v[i], i + 1, node, true, upbound);
}
upbound = maxBound(i + 1);
if (maxBound(i + 1) >= bestv)
addNode(cw, cv, i + 1, node, false,upbound);
node = que.top();
que.pop();
i = node->i;
cw = node->w;
cv = node->v;
upbound = node->upbound;
}
cout << bestv << endl;
int x[3] = { 0 };
for (int j = 2; j >= 0; j--)
{
x[j] = node->isleft == true ? 1 : 0;
node = node->parent;
}
for (int v : x)
{
cout << v << " ";
}
return 0;
}