裝載問題 – 分支限界法
裝載問題描述
分支限界法的搜索策略
在當前節點(擴展節點)處,先生成其所有的兒子節點(分支),然後再從當前的活節點(當前節點的子節點)表中選擇下一個擴展節點。爲了有效地選擇下一個擴展節點,加速搜索的進程,在每一個活節點處,計算一個函數值(限界),並根據函數值,從當前活節點表中選擇一個最有利的節點作爲擴展節點,使搜索朝着解空間上有最優解的分支推進,以便儘快地找出一個最優解。分支限界法解決了大量離散最優化的問題。
選擇方法
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/