一、分支限界算法思想
1. 分支限界法类似于回溯算法,是在问题的解空间树上搜索问题的算法,主要体现在两点不同:
- 求解目标不同。回溯算法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标是找出满足约束条件的一个解,或者是在满足约束条件的解中找出某种意义下的最优解。
- 搜索解空间树的方式不同。回溯算法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或者以最小耗费优先的方式搜索解空间树。
2. 分治限界算法基本思想:
分支限界法常以广度优先或者以最小耗费优先的方式搜索解空间树。在分支限界法中,每一个活节点只有一次机会称为扩展节点,活结点一旦称为扩展节点,每一个产生所有儿子节点(分支),在这些儿子节点中,导致不可行解或是导致非最优解的儿子节点会被舍弃掉,其余儿子节点会被加入活结点表中。
为了有效的选择下一个扩展节点加速搜索,在每一个活结点处计算一个函数值(限界),并根据计算的函数值结果从当前活结点表中去下一个最有利的节点称为扩展节点,使搜索朝着解空间树上最优解的分支推进。重复上述节点扩展过程,直到找到所需的最优解或者活结点表为空。
扩展节点:一个正在产生儿子的节点 活结点:一个自身已经生成,但其儿子还没有全部生成的节点 死结点:一个所有儿子已经产生的节点
深度优先搜索是对一个扩展节点R,一旦产生了它的一个儿子C,就把C当作新的扩展节点。在完成对子树C的深度搜索之后回溯到R时,将R重新变成扩展节点,继续生成R的下一个儿子。 广度优先搜索是在一个扩展节点R变成死节点之前,他一直在扩展节点。
从活节点表中选择下一个扩展节点时,不同的方式导致不同的分支限界法,常见有:
二、分支限界法的应用
1. 集装箱装载问题
有一批共n个集装箱要装上两艘载重量分别为c1,c2的轮船,其中集装箱i的重量为wi,且要求确定是否有一个合理的装载方案可将这n个集装箱装上这两艘轮船。
分析: 仅求出装载的最大重量
#include<iostream>
#include<queue>
using namespace std;
struct Node
{
Node(int l, int w)
{
level = l;
weight = w;
}
int level;
int weight;
};
int main()
{
int w[] = { 12,8,15 };
int c = 27;
int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int bestw = 0;
int i = 0;
queue<Node> que;
while (i < n)
{
if (cw + w[i] <= c)
{
if (cw + w[i] > bestw)
{
bestw = cw + w[i];
}
que.push(Node(i + 1, cw + w[i]));
}
que.push(Node(i + 1, cw));
Node node = que.front();
que.pop();
cw = node.weight;
i = node.level;
}
cout << bestw << endl;
}
打印出具体装入的集装箱
#include<iostream>
#include<queue>
using namespace std;
int w[] = { 12,8,15 };
int c = 27;
const int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int bestw = 0;
struct Node
{
Node(int w, int l, Node* p, bool left)
{
weight = w;
level = l;
parent = p;
isleft = left;
}
int level;
int weight;
Node* parent;
bool isleft;
};
int i = 0;
Node* node = nullptr;
queue<Node*> que;
Node* bestnode = nullptr;
void addLiveNode(int w, int level, Node* parent, bool isleft)
{
Node* node = new Node(w, level, parent, isleft);
que.push(node);
if (level == n && w == bestw)
{
bestnode = node;
}
}
int main()
{
while (i < n)
{
if (cw + w[i] <= c)
{
if (cw + w[i] > bestw)
{
bestw = cw + w[i];
}
addLiveNode(cw + w[i], i + 1, node, true);
}
addLiveNode(cw, i + 1, node, false);
node = que.front();
que.pop();
cw = node->weight;
i = node->level;
}
cout << bestw << endl;
int bestx[n] = { 0 };
for (int j = n - 1; j >= 0; j--)
{
bestx[j] = bestnode->isleft ? 1 : 0;
bestnode = bestnode->parent;
}
for (int v : bestx)
{
cout << v << " ";
}
}
添加剪枝操作,限界
#include<iostream>
#include<queue>
using namespace std;
int w[] = { 12,8,15 };
int c = 27;
const int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int bestw = 0;
int r = 0;
struct Node
{
Node(int w, int l, Node* p, bool left)
{
weight = w;
level = l;
parent = p;
isleft = left;
}
int level;
int weight;
Node* parent;
bool isleft;
};
int i = 0;
Node* node = nullptr;
queue<Node*> que;
Node* bestnode = nullptr;
void addLiveNode(int w, int level, Node* parent, bool isleft)
{
Node* node = new Node(w, level, parent, isleft);
que.push(node);
if (level == n && w == bestw)
{
bestnode = node;
}
}
int maxBound(int level)
{
int s = 0;
for (int j = level + 1; j < n; j++)
{
s += w[i];
}
return s;
}
int main()
{
while (i < n)
{
if (cw + w[i] <= c)
{
if (cw + w[i] > bestw)
{
bestw = cw + w[i];
}
addLiveNode(cw + w[i], i + 1, node, true);
}
r = maxBound(i);
if (cw + r >= bestw)
{
addLiveNode(cw, i + 1, node, false);
}
node = que.front();
que.pop();
cw = node->weight;
i = node->level;
}
cout << bestw << endl;
int bestx[n] = { 0 };
for (int j = n - 1; j >= 0; j--)
{
bestx[j] = bestnode->isleft ? 1 : 0;
bestnode = bestnode->parent;
}
for (int v : bestx)
{
cout << v << " ";
}
}
2. 0-1背包问题
- FIFO队列
#include<iostream>
#include<queue>
using namespace std;
int w[] = { 16,15,15 };
int v[] = { 45,25,25 };
int c = 30;
const int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int cv = 0;
int bestv = 0;
struct Node
{
Node(int w, int v, int l, Node* p, bool left)
{
weight = w;
value = v;
level = l;
parent = p;
isleft = left;
}
int weight;
int value;
int level;
Node* parent;
bool isleft;
};
int i = 0;
queue<Node*> que;
Node* bestnode = nullptr;
void addLiveNode(int w, int v, int l, Node* p, bool left)
{
Node* node = new Node(w, v, l, p, left);
que.push(node);
if (l == n && v == bestv)
{
bestnode = node;
}
}
int maxBound(int i)
{
int s = 0;
for (int j = i + 1; j < n; j++)
{
s += v[i];
}
return s;
}
int main()
{
Node* node = nullptr;
while (i < n)
{
if (cw + w[i] <= c)
{
if (cv + v[i] > bestv)
{
bestv = cv + v[i];
}
addLiveNode(cw + w[i], cv + v[i], i + 1, node, true);
}
int r = maxBound(i);
if (cv + r >= bestv)
{
addLiveNode(cw, cv, i + 1, node, false);
}
node = que.front();
que.pop();
cw = node->weight;
cv = node->value;
i = node->level;
}
int bestx[n] = { 0 };
for (int j = n - 1; j >= 0; j--)
{
bestx[j] = bestnode->isleft ? 1 : 0;
bestnode = bestnode->parent;
}
cout << bestv << endl;
for (int v : bestx)
{
cout << v << " ";
}
}
#include<iostream>
#include<queue>
#include<functional>
using namespace std;
int w[] = { 16,15,15 };
int v[] = { 45,25,25 };
int c = 30;
const int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int cv = 0;
int bestv = 0;
struct Node
{
Node(int w, int v, int l, int up, Node* p, bool left)
{
weight = w;
value = v;
level = l;
upbound = up;
parent = p;
isleft = left;
}
int weight;
int value;
int level;
int upbound;
Node* parent;
bool isleft;
};
int i = 0;
priority_queue<Node*, vector<Node*>, function<bool(Node*, Node*)>> que([](Node* n1, Node* n2)->bool {
return n1->upbound < n2->upbound;
});
void addLiveNode(int w, int v, int l, int up, Node* p, bool left)
{
Node* node = new Node(w, v, l, up, p, left);
que.push(node);
}
int maxBound(int i)
{
int s = cv;
for (int j = i; j < n; j++)
{
s += v[i];
}
return s;
}
int main()
{
Node* node = nullptr;
int upbound = maxBound(0);
while (i < n)
{
if (cw + w[i] <= c)
{
if (cv + v[i] > bestv)
{
bestv = cv + v[i];
}
addLiveNode(cw + w[i], cv + v[i], i + 1, upbound, node, true);
}
upbound = maxBound(i + 1);
if (upbound >= bestv)
{
addLiveNode(cw, cv, i + 1, upbound, node, false);
}
node = que.top();
que.pop();
cw = node->weight;
cv = node->value;
i = node->level;
upbound = node->upbound;
}
int bestx[n] = { 0 };
for (int j = n - 1; j >= 0; j--)
{
bestx[j] = node->isleft ? 1 : 0;
node = node->parent;
}
cout << bestv << endl;
for (int v : bestx)
{
cout << v << " ";
}
}
|