分枝定界(branch and bound)解旅行商(TSP)问题

[size=large]算法课的作业,C语言编写,分享一下。

linux下gcc编译通过(需要-std=c99参数)[/size]


输入:第一行为矩阵的大小,为 1~1000 的自然数,第二行以后是矩阵的值。例如:
[color=blue]5
0 14 30 5 6
10 0 11 4 3
4 6 0 5 6
15 10 13 0 2
13 3 4 11 0[/color]

输出:第一行是最优代价,第二行是路线。例如对上述输入,对应输出应为:
[color=blue]25
1 4 5 2 3 1[/color]


[size=large]附件中是测试数据[/size]


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#define INFINITY ((u_short) 0xFFFF)

#define INIT_CAPACITY 64

typedef unsigned int u_int;
typedef unsigned short u_short;

void printResult();

void init();
void clear();

void babSearch(u_int, u_int);
void babSearchLeft(u_int, u_int, u_int);
void babSearchRight(u_int, u_int, u_int);

u_int estimate();
u_int estLeft(u_int row, u_int col);
u_int estRight(u_int row, u_int col);

static u_int n;
static u_short *mat = 0;

static u_int selectedPathLen = 0;

static u_int bound = (u_int)0xFFFFFFFF;

static u_int origin = 0;

static u_int *path;

static struct{
u_int size;
bool *row;
bool *column;
} visited;

typedef struct{
u_int row;
u_int column;
} Node;

Node *newNode(u_int row, u_int column);
void deleteNode(Node *node);

inline Node *newNode(u_int row, u_int column){
Node *node = malloc(sizeof(Node));
node->row = row;
node->column = column;
return node;
}

inline void deleteNode(Node *node){
free(node);
}

typedef struct{
Node **nodes;
u_int size;
u_int capacity;
} Stack;

Stack stk;

void stack_init(Stack *s){
s->size = 0;
s->capacity = INIT_CAPACITY;
s->nodes = malloc(sizeof(Node *) * INIT_CAPACITY);
}

void stack_destroy(Stack *s){
free(s->nodes);
s->nodes = 0;
s->size = 0;
}

void stack_push(Stack *s, Node *n){
if(s->size == s->capacity){
s->capacity *= 2;
Node **tmp = malloc(sizeof(Node *) * s->capacity);
memcpy(tmp, s->nodes, sizeof(Node *) * s->size);
free(s->nodes);
s->nodes = tmp;
}
s->nodes[s->size++] = n;
}

Node* stack_pop(Stack *s){
Node *ret = 0;
if(s->size != 0){
ret = s->nodes[--s->size];
if(s->size == s->capacity / 3){
s->capacity /= 2;
Node **tmp = malloc(sizeof(Node *) * s->capacity);
memcpy(tmp, s->nodes, sizeof(Node *) * s->size);
free(s->nodes);
s->nodes = tmp;
}
}
return ret;
}


void babSearch(u_int currentValue, u_int start){
if(visited.size == n-1){ //all nodes are visited
u_int row_l;
u_int col_l;
for(row_l = 0; row_l < n; ++row_l){
if(!visited.row[row_l]) break;
}
for(col_l = 0; col_l < n; ++col_l){
if(!visited.column[col_l]) break;
}
stack_push(&stk, newNode(row_l, col_l));
if(currentValue < bound){
bound = currentValue;
for(u_int i = 0; i < stk.size; ++i){
//path[i] = stk.nodes[i]->row;
path[i] = stk.nodes[i]->row + 1;
}
}
deleteNode(stack_pop(&stk));
return;
}

int ci = -1;
u_short min = INFINITY;
for(u_int j = 0; j < n; ++j){
if(visited.column[j]) continue;
if(mat[start*n+j] < min){
min = mat[start*n+j];
ci = j;
}
}

if(ci == -1){ //WHY?!
return;
}

u_int el = estLeft(start, ci);
u_int er = estRight(start, ci);

if(el <= er){ //goto left first
if(el < bound){
babSearchLeft(el, start, ci);
}
if(er < bound){
babSearchRight(er, start, ci);
}
}else{ //goto right first
if(er < bound){
babSearchRight(er, start, ci);
}
if(el < bound){
babSearchLeft(el, start, ci);
}
}
}

inline void babSearchLeft(u_int currentValue, u_int row, u_int col){
stack_push(&stk, newNode(row, col));

selectedPathLen += (u_int)mat[row*n+col];

u_short raw = mat[col*n+col];
mat[col*n+col] = INFINITY;
visited.row[row] = true;
visited.column[col] = true;
visited.size++;

babSearch(currentValue, col);

mat[col*n+col] = raw;
visited.row[row] = false;
visited.column[col] = false;
visited.size--;

selectedPathLen -= (u_int)mat[row*n+col];

deleteNode(stack_pop(&stk));
}

inline void babSearchRight(u_int currentValue, u_int row, u_int col){
u_short raw = mat[row*n+col];
mat[row*n+col] = INFINITY;

babSearch(currentValue, row);

mat[row*n+col] = raw;
}

inline u_int estLeft(u_int row, u_int col){
selectedPathLen += (u_int)mat[row*n+col];

visited.row[row] = true;
visited.column[col] = true;
visited.size++;

u_int ret = estimate() + selectedPathLen;

visited.row[row] = false;
visited.column[col] = false;
visited.size--;

selectedPathLen -= (u_int)mat[row*n+col];

return ret;
}

inline u_int estRight(u_int row, u_int col){
u_int raw = mat[row*n+col];
mat[row*n+col] = INFINITY;

u_int ret = estimate() + selectedPathLen;

mat[row*n+col] = raw;

return ret;
}


static u_short *minEachRow = 0;
static bool *usedColumn = 0;

u_int estimate(){
u_int ret = 0;
memset(minEachRow, 0, sizeof(u_short) * n);
memset(usedColumn, 0, sizeof(bool) * n);
for(u_int i = 0; i < n; ++i){
if(visited.row[i]) continue; //deleted row
u_short min = INFINITY;
u_int columnIndex = 0;
for(u_int j = 0; j < n; ++j){
if(visited.column[j]) continue; //deleted column
if(mat[i*n+j] < min){
min = mat[i*n+j];
columnIndex = j;
}
}
if(min == INFINITY){
ret = INFINITY;
goto L;
}
ret += min;
minEachRow[i] = min;
usedColumn[columnIndex] = true;
}
for(u_int j = 0; j < n; ++j){
if(visited.column[j]) continue; //deleted column
if(!usedColumn[j]){
u_short min = INFINITY;
for(u_int i = 0; i < n; ++i){
if(visited.row[i]) continue; //deleted row
if(mat[i*n+j] == INFINITY) continue;
u_int r = (u_int)mat[i*n+j] - (u_int)minEachRow[i];
if(r < min){
min = r;
}
}
if(min == INFINITY){
ret = INFINITY;
goto L;
}
ret += min;
}
}

L:
return ret;
}

void init(){
scanf("%u", &n);

visited.size = 0;
visited.row = malloc(sizeof(bool) * n);
visited.column = malloc(sizeof(bool) * n);
for(u_int i = 0; i < n; ++i){
visited.row[i] = false;
visited.column[i] = false;
}

path = malloc(sizeof(u_int) * n);

mat = malloc(sizeof(u_short) * n * n);
u_int i = 0;
while(i < n * n){
scanf("%u", &mat[i]);
if(mat[i] == 0){
mat[i] = INFINITY;
}
++i;
}

stack_init(&stk);

minEachRow = malloc(sizeof(u_short) * n);
usedColumn = malloc(sizeof(bool) * n);
}

void clear(){
free(visited.row);
free(visited.column);

free(path);

if(mat != 0){
free(mat);
mat = 0;
}

stack_destroy(&stk);

free(minEachRow);
free(usedColumn);
}

void printResult(){
printf("%d\n", bound);
for(u_int i = 0; i < n; ++i){
printf("%d ", path[i]);
}
printf("%d\n", path[0]);
}

int main(void){
init();
babSearch(0, origin);
printResult();
clear();
return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
支限界法类又称为剪枝限界法或分支定界法,它类似于回溯法,也是一种在问题空间树T上搜索问题的算法。它与回溯法有两点不同:①回溯法只通过约束条件剪去非可行,而分支限界法不仅通过约束条件,而且通过目标函数的限界来减少无效搜索,也就是剪掉了某些不包含最优的可行。②在空间树上的搜索方式也不相同。回溯法以深度优先的方式搜索空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索空间树。分支限界法的搜索策略是:在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展结点。为了有效地选择下一扩展结点,以加速搜索的进程, 在每一活结点处,计算一个函数值(限界),并根据这些已计算出的函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着空间树上有最优的分支推进,以便尽快地找出一个最优。 从活结点表中选择下一扩展结点的不同方式导致不同的分支限界法。最常见的有以下两种方式: ①队列式(FIFO)分支限界法:队列式分支限界法将活结点表组织成一个队列,并按队列的先进先出原则选取下一个结点为当前扩展结点。 ②优先队列式分支限界法:优先队列式分支限界法将活结点表按照某个估值函数C(x)的值组织成一个优先队列,并按优先队列中规定的结点优先级选取优先级最高的下一个结点成为当前扩展结点。 影响分支限界法搜索效率的有两个主要因素:一是优先队列Q的优先级由C(x)确定,它能否保证在尽可能早的情况下找到最优,如果一开始找到的就是最优,那么搜索的空间就能降低到最小。二是限界函数u(x),它越严格就越可能多地剪去分支,从而减少搜索空间。 在用分支限界法TSP问题时,有不少很好的限界函数和估值函数已经构造出来出了(限于篇幅,这里不做详细介绍), 使得分支限界法在大多数情况下的搜索效率大大高于回溯法。但是,在最坏情况下,该算法的时间复杂度仍然是O(n!),而且有可能所有的(n-1)!个结点都要存储在队列中。 近似算法是指不能肯定找到最优的算法,但通常找到的也是比较好的,或称近似最优。[20]一般而言,近似算法的时间复杂度较低,通常都是多项式时间内的。由于近似算法的时间效率高,所以在实际应用中,主要是使用近似算法,这一类算法也一直是研究的主要对象。传统的近似算法以采用贪心策略和局部搜索为主,而几十年来,随着以遗传算法为代表的新型启发式搜索算法的逐步完善,在TSP问题上获得了巨大的成功。遗传算法、模拟退火算法、蚁群算法等已经成为公认的好算法。在本节中,将介绍传统的近似算法。
分枝定界法是一种求整数线性规划问题的常用方法。该方法的基本思想是将整数线性规划问题不断分成子问题,然后对每个子问题进行求,直到得到最优Python 实现整数线性规划的分枝定界法,可以基于 Python 的优化库 `PuLP`。以下是一个简单的示例代码: ```python from pulp import * # 定义整数线性规划问题 prob = LpProblem("Integer Linear Programming", LpMinimize) # 添加变量 x1 = LpVariable("x1", lowBound=0, cat='Integer') x2 = LpVariable("x2", lowBound=0, cat='Integer') # 添加目标函数 prob += 3*x1 + 4*x2 # 添加约束条件 prob += 2*x1 + x2 >= 3 prob += x1 + 2*x2 >= 3 # 求 prob.solve() # 输出结果 print("Status:", LpStatus[prob.status]) print("Minimum value:", value(prob.objective)) print("Optimal solution:") for v in prob.variables(): print(v.name, "=", v.varValue) ``` 以上代码是一个简单的整数线性规划问题的求示例,其中定义了两个变量 `x1` 和 `x2`,并添加了目标函数和两个约束条件。通过 `prob.solve()` 方法进行求,并输出最优及对应的变量取值。 分枝定界法的实现,可以通过递归地将当前问题成两个子问题,并对子问题进行求,直到得到最优。在每次分时,需要对当前的上下界进行计算,并通过 `prob +=` 方法添加上下界约束条件。具体实现可以参考以下示例代码: ```python from pulp import * # 定义整数线性规划问题 prob = LpProblem("Integer Linear Programming", LpMinimize) # 添加变量 x1 = LpVariable("x1", lowBound=0, cat='Integer') x2 = LpVariable("x2", lowBound=0, cat='Integer') # 添加目标函数 prob += 3*x1 + 4*x2 # 添加初始约束条件 prob += 2*x1 + x2 >= 3 prob += x1 + 2*x2 >= 3 def solve_branch_and_bound(prob): # 求当前问题 prob.solve() # 获取当前的上下界 upper_bound = value(prob.objective) lower_bound = upper_bound # 分问题并求 for v in prob.variables(): # 向上取整 upper_value = int(v.varValue) + 1 # 向下取整 lower_value = int(v.varValue) # 分为两个子问题 prob1 = LpProblem("Integer Linear Programming", LpMinimize) prob2 = LpProblem("Integer Linear Programming", LpMinimize) # 添加变量 x1 = LpVariable("x1", lowBound=0, cat='Integer') x2 = LpVariable("x2", lowBound=0, cat='Integer') # 添加目标函数 prob1 += 3*x1 + 4*x2 prob2 += 3*x1 + 4*x2 # 添加约束条件 for c in prob.constraints: prob1 += c prob2 += c # 添加上下界约束条件 if v.name == 'x1': prob1 += x1 >= upper_value prob2 += x1 <= lower_value elif v.name == 'x2': prob1 += x2 >= upper_value prob2 += x2 <= lower_value # 求问题 prob1.solve() prob2.solve() # 计算上下界 if value(prob1.objective) < value(prob2.objective): upper_bound = min(upper_bound, value(prob1.objective)) else: upper_bound = min(upper_bound, value(prob2.objective)) lower_bound = max(lower_bound, value(prob1.objective), value(prob2.objective)) # 判断是否达到最优 if upper_bound == lower_bound: return upper_bound # 分问题并求 for v in prob.variables(): # 向上取整 upper_value = int(v.varValue) + 1 # 向下取整 lower_value = int(v.varValue) # 分为两个子问题 prob1 = LpProblem("Integer Linear Programming", LpMinimize) prob2 = LpProblem("Integer Linear Programming", LpMinimize) # 添加变量 x1 = LpVariable("x1", lowBound=0, cat='Integer') x2 = LpVariable("x2", lowBound=0, cat='Integer') # 添加目标函数 prob1 += 3*x1 + 4*x2 prob2 += 3*x1 + 4*x2 # 添加约束条件 for c in prob.constraints: prob1 += c prob2 += c # 添加上下界约束条件 if v.name == 'x1': prob1 += x1 >= upper_value prob2 += x1 <= lower_value elif v.name == 'x2': prob1 += x2 >= upper_value prob2 += x2 <= lower_value # 判断是否需要继续分 if min(value(prob1.objective), value(prob2.objective)) > upper_bound: continue # 递归求问题 if value(prob1.objective) <= value(prob2.objective): upper_bound = min(upper_bound, solve_branch_and_bound(prob1)) else: upper_bound = min(upper_bound, solve_branch_and_bound(prob2)) return upper_bound # 求 optimal_value = solve_branch_and_bound(prob) # 输出结果 print("Minimum value:", optimal_value) print("Optimal solution:") for v in prob.variables(): print(v.name, "=", v.varValue) ``` 以上代码实现了整数线性规划的分枝定界法,其中 `solve_branch_and_bound` 方法用于递归地分问题并求。在每次分时,都会计算当前的上下界,并根据上下界约束条件进行分。通过递归求问题,最终得到最优
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值