装载问题 – 分支限界法
装载问题描述
分支限界法的搜索策略
在当前节点(扩展节点)处,先生成其所有的儿子节点(分支),然后再从当前的活节点(当前节点的子节点)表中选择下一个扩展节点。为了有效地选择下一个扩展节点,加速搜索的进程,在每一个活节点处,计算一个函数值(限界),并根据函数值,从当前活节点表中选择一个最有利的节点作为扩展节点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。分支限界法解决了大量离散最优化的问题。
选择方法
1.队列式(FIFO)分支限界法
队列式分支限界法将活节点表组织成一个队列,并将队列的先进先出原则选取下一个节点为当前扩展节点。
2.优先队列式分支限界法
优先队列式分支限界法将活节点表组织成一个优先队列,并将优先队列中规定的节点优先级选取优先级最高的下一个节点成为当前扩展节点。如果选择这种选择方式,往往将数据排成最大堆或者最小堆来实现。
数据结构的选择
typedef struct {
int no; // 结点标号
int id; // 节点id
int sw; // 背包中物品的重量
int sv; // 背包中物品的价值
double prior; // sv/sw
}Node;
FIFO
/**
* 先进先出的分支限界
typedef struct {
int no; // 结点标号
int id; // 节点id
int sw; // 背包中物品的重量
int sv; // 背包中物品的价值
double prior; // sv/sw
}Node;
搜索树:左边;放 右边;不放
* @param w 物品的重量
* @param v 物品的价值
* @param c 背包承受最大重量
* @param n 物品个数
*/
void branchknapFIFO(int *w,int*v,int n,int c) {
int bestv = 0;
int f = 0;
int r = 0;
Node que[3000];
int path[15];
que[0].no = 1;
que[0].id = que[0].sv = que[0].sw = 0;
while(f <= r) {
Node node = que[f];
// printf("%d %d %d %d\n",node.id+1,node.no,node.sw,node.sv);
if(node.no >= pow(2,n)) {
if(node.sv > bestv) {
bestv = node.sv;
printf("bestv=%d, bestx=[",bestv);
int temp = node.no;
int i = 0;
while(temp > 1) {
if(temp % 2 == 0)
path[i] = 1;
else
path[i] = 0;
temp /= 2;
i++;
}
i--;
while(i >= 0) {
printf(" %d",path[i]);
i--;
}
printf(" ]\n");
}
} else {
if((node.sw + w[node.id + 1]) <= c) {
r++;
que[r].id = node.id + 1;
que[r].no = node.no * 2;
int id = node.id + 1;
que[r].sv = node.sv + v[id];
que[r].sw = node.sw + w[id];
}else {
r++;
que[r].id = node.id + 1;
que[r].no = node.no * 2 + 1;
que[r].sv = node.sv;
que[r].sw = node.sw;
}
}
f++;
}
}
优先队列
/**
* 最大价值的分支限界
typedef struct {
int no; // 结点标号
int id; // 节点id
int sw; // 背包中物品的重量
int sv; // 背包中物品的价值
double prior; // sv/sw
}Node;
搜索树:左边;放 右边;不放
* @param w 物品的重量
* @param v 物品的价值
* @param c 背包承受最大重量
* @param n 物品个数
*/
void branchknapMAXCOST(int *w,int *v,int c,int n) {
int bestv = 0;
int f = 0; //队尾
int r = 0; //对头
Node que[3000];
memset(que,0,sizeof(que));
int path[15];
//队列加入当前活结点
que[0].no = 1;
que[0].id = que[0].sv = que[0].sw = que[0].prior = 0;
while(f <= r) {
Node node = que[f];
//打印当前搜索情况
// printf("%d %d %d %d\n",node.id+1,node.no,node.sw,node.sv);
//到达底层
if(node.no >= pow(2,n)) {
//更新bestv和path路径
if(node.sv > bestv) {
bestv = node.sv;
printf("bestv=%d, bestx=[",bestv);
//根据节点标号提取路径
int temp = node.no;
int i = 0;
while(temp > 1) {
if(temp % 2 == 0)
path[i] = 1;
else
path[i] = 0;
temp /= 2;
i++ ;
}
i--;
while(i >= 0) {
printf(" %d",path[i]);
i--;
}
printf(" ]\n");
}
} else {
//剪枝函数
//加入左边节点
if((node.sw + w[node.id + 1]) <= c && surplusValue(v,n,node.id+1) + node.sv > bestv) {
r++;
que[r].id = node.id + 1;
que[r].no = node.no*2;
int id = node.id + 1;
que[r].sv = node.sv + v[id];
que[r].sw = node.sw + w[id];
que[r].prior = que[r].sv / (que[r].sw*1.0);
}
//加入右边节点
if(surplusValue(v,n,node.id+1) + node.sv > bestv) {
r++;
que[r].id = node.id + 1;
que[r].no = node.no*2 + 1;
que[r].sv = node.sv;
que[r].sw = node.sw;
que[r].prior = node.prior;
}
}
f++;
qsort(que,f,r);
}
}
int surplusValue(int *v,int n,int y) {
int sum = 0;
for(int i = y; i <= n; i++) {
sum += v[i];
}
return sum;
}
//模拟优先队列排序
void qsort(Node *que,int l,int r) {
int len = r - l + 1;
int flag;
for(int i = 0; i < len; i ++) {
flag = 0;
for(int j = l; j < l + len - i; j++) {
if(que[j].prior < que[j+1].prior) {
Node t = que[j];
que[j] = que[j+1];
que[j+1] = t;
flag = 1;
}
}
//if(!flag ) return;
}
}
源代码
https://paste.ubuntu.com/p/V6fhGN4Drb/