货郎担问题(分支限界法)

#include<iostream>
#include<fstream>
using namespace std;

typedef int Type;
#define N 5
#define ZERO_VALUE_OF_TYPE 0
#define MAX_VALUE_OF_TYPE 2147483647

typedef struct node_data{
	Type c[N][N];       //费用矩阵
	int row_init[N];    //费用矩阵的当前行映射为原始行
	int col_init[N];    //费用矩阵的当前列映射为原始列
	int row_cur[N];     //费用矩阵的当前行映射为原始行
	int col_cur[N];     //费用矩阵的当前列映射为原始列
	int ad[N];          //回路顶点邻接表
	int k;              //当前费用矩阵的阶
	Type w;             //节点的下界
	struct node_data *next;    //队列链指针
}NODE;

Type c[N][N];
Type ad[N];
/*
NODE *xnode;          //父亲结点指针
NODE *ynode;          //儿子结点指针
NODE *znode;          //儿子结点指针
NODE *qbase;          //优先队列首指针
Type bound;          //当前可行解的最优值
*/

//计算node所指向结点的费用矩阵行row最小值
Type row_min(NODE *node,int row,Type &second)
{
	Type temp;
	int i;
	int flag=0;
	if(node->c[row][0] < node->c[row][1]){
		temp=node->c[row][0];
		second=node->c[row][1];
	}
	else{
		temp=node->c[row][1];
		second=node->c[row][0];
	}
	for(i=2;i<node->k;i++){
		if(node->c[row][i]<temp){
			second=temp;
			temp=node->c[row][i];
		}
		else if(node->c[row][i]<second)
			second = node->c[row][i];
	}

	for(i=0;i<node->k;i++)   //如果整行都是最大值则不进行归约,列同
		if(node->c[row][i]==MAX_VALUE_OF_TYPE)
			flag++;
	if(node->k==flag)
		return 0;
	return temp;
}

//计算node所指向结点的费用矩阵列col最小值
Type col_min(NODE *node,int col,Type &second)
{
	Type temp;
	int i;
	int flag=0;
	if(node->c[0][col] < node->c[1][col]){
		temp=node->c[0][col];
		second=node->c[1][col];
	}
	else{
		temp=node->c[1][col];
		second=node->c[0][col];
	}
	for(i=2;i<node->k;i++){
		if(node->c[i][col]<temp){
			second = temp;
			temp = node->c[i][col];;
		}
		else if(node->c[i][col]<second)
			second=node->c[i][col];
	}

	for(i=0;i<node->k;i++)
		if(node->c[i][col]==MAX_VALUE_OF_TYPE)
			flag++;
	if(node->k==flag)
		return 0;
	return temp;
}


//归约node所指向结点的费用矩阵
Type array_red(NODE *node)
{
	int i,j;
	Type temp,temp1,sum=ZERO_VALUE_OF_TYPE;
	for(i=0;i<node->k;i++){         //行归约
		temp=row_min(node,i,temp1);  //行归约常数
		for(j=0;j<node->k;j++){
			if(node->c[i][j]==MAX_VALUE_OF_TYPE)  //跳过最大值
				continue;
			node->c[i][j]-=temp;
		}
		sum += temp;                   //行归约常数累计
	}
	for(j=0;j<node->k;j++){         //列归约
		temp=col_min(node,j,temp1);  //列归约常数
		for(i=0;i<node->k;i++){
			if(node->c[i][j]==MAX_VALUE_OF_TYPE)  //跳过最大值
				continue;
			node->c[i][j]-=temp;
		}
		sum+=temp;                   //列归约常数累计
	}	
	return sum;                      //返回归约常数


}


//计算node所指向结点的费用矩阵Dkl,选择搜索分支的边
Type edge_sel(NODE *node,int &vk,int &vl)
{
	int i,j;
	Type temp,d=ZERO_VALUE_OF_TYPE;
	Type *row_value = new Type[node->k];
	Type *col_value = new Type[node->k];
	for(i=0;i<node->k;i++)                  //每一行的次小值
		row_min(node,i,row_value[i]);
	for(i=0;i<node->k;i++)                  //每一列的次小值
		col_min(node,i,col_value[i]);
	for(i=0;i<node->k;i++){     //对费用矩阵所有为0的元素计算相应的temp值
		for(j=0;j<node->k;j++){
			if(node->c[i][j]==ZERO_VALUE_OF_TYPE){
				temp=row_value[i]+col_value[j];
				if(temp>d){               //求最大的temp值d
					d=temp;
					vk=i;
					vl=j;                //保存相应的行列号
				}
			}
		}
	}
	delete row_value;
	delete col_value;
	return d;
}


//删除node所指向结点的费用矩阵第vk行第vl列的所有元素
void del_rowcol(NODE *node,int vk,int vl)
{
	int i,j,vk1,vl1;
	for(i=vk;i<node->k-1;i++)      //元素上移
		for(j=0;j<vl;j++)
			node->c[i][j] = node->c[i+1][j];
	for(j=vl;j<node->k-1;j++)      //元素左移
		for(i=0;i<vk;i++)
			node->c[i][j] = node->c[i][j+1];
	for(i=vk;i<node->k-1;i++)      //元素上移及左移
		for(j=vl;j<node->k-1;j++) 
			node->c[i][j] = node->c[i+1][j+1];
	vk1 = node->row_init[vk];   //当前行vk转换为原始行vk1
	node->row_cur[vk1] = -1;    //原始行删除标志
	for(i=vk1+1;i<N;i++)        //vk1之后的原始行,其对应的行号减一
		node->row_cur[i]--;
	vl1 = node->col_init[vl];   //当前列vl转换为原始列vl1
	node->col_cur[vl1]=-1;      //原始列删除标志
	for(i=vl1+1;i<N;i++)        //vl1之后的原始列,其对应的列号减一
		node->col_cur[i]--;
	for(i=vk;i<node->k-1;i++){  //修改vk及其之后的当前行的对应原始行号
		node->row_init[i] = node->row_init[i+1];
	}
	for(i=vl;i<node->k-1;i++)   //修改vl及其之后的当前列的对应原始列号
		node->col_init[i] = node->col_init[i+1];
	node->k--;                  //当前矩阵的阶数减一
}


//登记回路顶点邻接表,旁路有关的边
void edg_byp(NODE *node,int vk,int vl)
{
	int k,l;
	vk=node->row_init[vk];            //当前行号转换为原始行号
	vl=node->col_init[vl];             //当前列号转换为原始列号
	node->ad[vk]=vl;             //登记回路顶点邻接表
	k=node->row_cur[vl];        //vl转换为当前行号k
	l=node->col_cur[vk];        //vk转换为当前列号l
	if((k>0)&&(l>=0)){     //当行、列号均处于当前矩阵中旁路相应的边
		node->c[k][l] = MAX_VALUE_OF_TYPE;
	}
}

//初始化
NODE *initial(Type c[][N],int n)
{
	int i,j;
	NODE *node=new NODE;       //分配结点缓冲区
	for(i=0;i<n;i++)           //复制费用矩阵的初始数据
		for(j=0;j<n;j++){
			node->c[i][j]=c[i][j];
		}

		for(i=0;i<n;i++){       //建立费用矩阵原始行、列号与初始行、列号的关系
			node->row_init[i]=i;
			node->col_init[i]=i;
			node->row_cur[i]=i;
			node->col_cur[i]=i;
		} 
	for(i=0;i<n;i++){          //回路顶点邻接表初始化为空
		node->ad[i]=-1;
	}
	node->k=n; 
	return node;	          //返回结点指针
}

void Q_insert(NODE *qbase,NODE *xnode){
	NODE *p;
	if(qbase->next==NULL){
		qbase->next=xnode;
		xnode->next=NULL;
	}
	else if(xnode->w < qbase->next->w){
		xnode->next=qbase->next;
		qbase->next=xnode;
	}
	else{
		p=qbase->next;
		while(p->next){
			if((xnode->w > p->w)&&(xnode->w < p->next->w)){
				xnode->next=p->next;
				p->next=xnode;
				break;
			}
			p=p->next;
		}
		if(p->next==NULL){
			p->next=xnode;
			xnode->next=NULL;
		}
	}
	p=qbase->next;
	
}

NODE *Q_delete(NODE *qbase){
	NODE *p;
	p=qbase->next;
	qbase->next=p->next;
	return p;
}

//货郎担问题的分支限界算法
template <class Type>
Type traveling_salesman(Type c[][N],int n,int ad[])
{
	int i,j,vk,vl;
	Type d,w,bound=MAX_VALUE_OF_TYPE;
	NODE *xnode,*ynode,*znode,*qbase;
	qbase=new NODE;
	qbase->next=NULL;
	xnode=initial(c,n);        //初始化父亲结点		
	xnode->w = array_red(xnode);   //归约费用矩阵
	while(xnode->k!=0){			
		d=edge_sel(xnode,vk,vl);     //选择分支方向并计算Dkl
		znode=new NODE;             //建立分支结点(右儿子结点)
		*znode = *xnode;              //x结点的数据复制到z结点
		znode->c[vk][vl]=MAX_VALUE_OF_TYPE;	 //旁路Z结点的边		
					
		array_red(znode);            //归约z结点的费用矩阵		
		znode->w = xnode->w + d;       //计算z结点的下界
		if(znode->w<bound)             //若下界小于当前可行解最优值
			Q_insert(qbase,znode);     //z结点插入优先队列	
		else delete znode;             //否则剪去该结点		
		ynode=new NODE;             //建立分支结点(左儿子结点)
		*ynode=*xnode;              //x结点数据复制到y结点
		edg_byp(ynode,vk,vl);       //登记回路顶点的邻接表,旁路有关的边
		ynode->c[vl][vk]=MAX_VALUE_OF_TYPE;	
		del_rowcol(ynode,vk,vl);    //删除y结点费用矩阵当前vk行vl列
								
		ynode->w = array_red(ynode);  //归约y结点费用矩阵		
		ynode->w += xnode->w;        //计算y结点的下界
		if(ynode->k==2){             //费用矩阵只剩下2阶
			if((ynode->c[0][0] == ZERO_VALUE_OF_TYPE)&&
				(ynode->c[1][1] == ZERO_VALUE_OF_TYPE)){
				ynode->ad[ynode->row_init[0]] = ynode->col_init[0];
				ynode->ad[ynode->row_init[1]] = ynode->col_init[1];
			}
			else{
				ynode->ad[ynode->row_init[0]] = ynode->col_init[1];
				ynode->ad[ynode->row_init[1]] = ynode->col_init[0];
			}                         //登记最后两条边
			ynode->k=0;
		}
		if(ynode->w<bound){          //若下界小于当前可行解最优值
			Q_insert(qbase,ynode);   //y结点插入优先队列
			if(ynode->k==0)          //更新当前可行解最优值
				bound = ynode->w;
		}
		else delete ynode;    //否则剪去y结点
		xnode=Q_delete(qbase);   //取优先队列首元素
	} 
	w=xnode->w;               //保存最短路线费用
	for(i=0;i<n;i++){         //保存路线的顶点邻接表
		ad[i]=xnode->ad[i];
	}
	delete xnode;              //释放x结点的缓冲区
	while(qbase->next){             //释放队列结点缓冲区
		xnode = Q_delete(qbase);
		delete xnode;
	} 
	return w;            //回送最短路线费用
}

void main(){
	int i,j;
	ifstream fin("1.txt");
	if(!fin){
		cout<<"cannpt open file!"<<endl;
		exit(0);
	}
	for(i=0;i<N;i++)
		for(j=0;j<N;j++){
			fin>>c[i][j];
		}
	fin.close();
	cout<<"最小费用为:\n";
	cout<<traveling_salesman(c,N,ad)<<endl;
	cout<<"回路顶点邻接表登记情况为:"<<endl;
	cout<<"\t";
	for(i=0;i<N;i++)
		cout<<i<<"\t";
	cout<<endl;
	cout<<"ad:\t";
	for(i=0;i<N;i++)
		cout<<ad[i]<<"\t";
	cout<<endl;
}

fiel(1.text):
在这里插入图片描述
运行结果:
在这里插入图片描述

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用分支限界法求解货郎问题的伪码: ``` class City: def __init__(self, name, distance): self.name = name self.distance = distance def tsp(cities): n = len(cities) path = [0] * (n + 1) visited = [False] * n best_path = [0] * (n + 1) best_distance = float('inf') def calculate_distance(path): total_distance = 0 for i in range(n): total_distance += cities[path[i]].distance[path[i+1]] return total_distance def branch_and_bound(curr_city, curr_distance, depth): nonlocal best_distance, best_path if depth == n: curr_distance += cities[curr_city].distance[0] if curr_distance < best_distance: best_distance = curr_distance best_path = path.copy() for i in range(1, n): if not visited[i]: path[depth] = i visited[i] = True new_distance = curr_distance + cities[curr_city].distance[i] if new_distance < best_distance: branch_and_bound(i, new_distance, depth + 1) visited[i] = False branch_and_bound(0, 0, 0) return best_path, best_distance ``` 这段伪码实现了一个`City`类来表示每个城市,其中包含了城市的名称和与其他城市的距离。`tsp`函数使用分支限界法来解决货郎问题。它通过递归地在每个节点处进行分支和限界来搜索最优路径。`path`列表用于保存当前的路径,`visited`列表用于标记已经访问过的城市。`best_path`和`best_distance`分别用于保存最优路径和最小距离。`calculate_distance`函数用于计算给定路径的总距离。`branch_and_bound`函数是核心的分支限界算法实现,它通过遍历每个未访问过的城市进行分支,并在每个节点处进行限界剪枝判断。最终,函数返回最优路径和最小距离。 请注意,这只是一个简化的伪码示例,实际的实现可能需要更多的优化和边界条件处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值