分支限界法求解TSP问题

实验名称
分支限界法求解TSP问题。

实验目的
分支限界法求解TSP问题。

实验原理
对于TSP,我们需要利用上界和下界来对BFS进行剪枝,通过不断更新上界和下界,尽可能的排除不符合需求的child,以实现剪枝。最终,当上限和下限等同时,我们可以获得最优的BFS解,以解决TSP问题。

实验步骤
①根据限界函数计算目标函数的下界;采用贪心法得到上界;
②将待处理结点表PT初始化为空;
③从顶点1出发求解TSP问题;
④while (k>=1):
i=k+1;
x[i]=1;
while (x[i]<=n):
如果路径上顶点不重复,则计算目标函数值lb;
如果lb<=up,将路径上的顶点和lb值存储在表PT中;
x[i]=x[i]+1;
若i=n且叶子结点的目标函数值在表PT中最小,则将该叶子结点对应的最优解输出;
否则,若i=n,则在表PT中取叶子结点的目标函数值最小的结点lb,令up=lb,将表PT中目标函数值lb超出up的结点删除;
k=表PT中lb最小的路径上顶点个数。

实验心得
通过这次实验,我学习了分支限界法,回顾了TSP问题,同时巩固了随机数据生成方法和文件读写操作的知识。

#include<iostream>
#include<algorithm>
#include<queue>
#include<fstream>
#include<ctime>
#define MAXLENGTH 10
using namespace std;
int n;//城市个数
int value[MAXLENGTH][MAXLENGTH];//城市距离代价矩阵
bool dfs_visited[MAXLENGTH];
int up;
int down;
void Modify();
int solve();
void get_up();
void get_down();
int dfs(int, int, int);

struct  Node
{
	bool visited[MAXLENGTH];
	int cixu[MAXLENGTH];
	int start;
	int start_p;
	int end;
	int end_p;
	int k;
	int sumv;
	int lb;
	bool operator <(const Node &p) const
	{
		return p.lb < lb;
	}
};

Node tmp;
int result[MAXLENGTH];
int times=0;
int get_lb(Node);
priority_queue<Node> pq;
int main()
{
	ifstream in("input.txt");
	in>>n;//城市个数 
	for (int i = 1; i <= n; i++)//城市距离代价矩阵
	{
		for (int j = 1; j <= n; j++)
		{
			in>>value[i][j];
		}
	}
	in.close(); 
	ofstream out("output.txt");
	clock_t start,end;
    start=clock();
	Modify();
	cout<<"最短路径长度为"<<solve()<<endl;
	end=clock();
    printf("时间:%fms\n",(double)(end-start)/CLK_TCK);
	for(int i=1;i<=n;i++)
	{
		out<<result[i];
	}
	out.close(); 
	return 0;
}
void Modify()
{
	for (int i = 1; i <= n; i++)
	{
		value[i][i] = 999;
	}
}

int solve()
{
	get_up();
	cout << "上界:" << up << endl;
	get_down();
	cout << "下界:" << down << endl;
	Node star;
	star.start = 1;
	star.end = 1;
	star.k = 1;
	for (int i = 1; i <= n; i++)
	{
		star.visited[i] = false;
		star.cixu[i] = 1;
	}
	star.visited[1] = true;
	star.cixu[1] = 1;
	star.sumv = 0;
	star.lb = down;
	int ret = 999;
	pq.push(star);
	cout << "start:lb=" << star.lb << endl;
	while (pq.size())
	{
		tmp = pq.top();
		pq.pop();
		if (tmp.k==n-1)
		{
			int p;
			for (int i = 1; i <= n; i++)
			{
				if (!tmp.visited[i])
				{
					p = i;
					tmp.cixu[tmp.k + 1] = i;
					break;
				}
			}
			int ans = tmp.sumv + value[tmp.end][p] + value[p][tmp.start];
			if (ans <= tmp.lb){
				ret = min(ans, ret);
				for (int j = 1; j <= n; j++)
				{
					result[j] = tmp.cixu[j];
				}
			}
			else
			{
				up = min(ret, ans);
				ret = min(ret, ans);
				continue;
			}
		}
		Node next;
		for (int i = 1; i <= n; i++)
		{
			if (!tmp.visited[i])
			{
				next.start = tmp.start;
				next.sumv = tmp.sumv + value[tmp.end][i];
				int tmpstart = tmp.end;
				next.end = i;
				next.k = tmp.k + 1;
				for (int j = 1; j <= n; j++)
				{
					next.visited[j] = tmp.visited[j];
					next.cixu[j] = tmp.cixu[j];
				}
				next.visited[i] = true;
				next.cixu[next.k] = i;
				next.lb = get_lb(next);
				if (next.lb > up)continue;
				pq.push(next);
				cout << tmpstart << "->" << next.end << ":lb=" << next.lb << endl;
			}
		}
	}
	return ret;
}

void get_up()
{
	dfs_visited[1] = true;
	up = dfs(1, 1, 0);
}

int dfs(int u, int k, int l)
{
	int minlen = 999;
	int p;
	if (k==n)return l + value[u][1];
	for (int i = 1; i <= n; i++)
	{
		if (!dfs_visited[i]&&value[u][i]<minlen)
		{
			minlen = value[u][i];
			p = i;
		}
	}
	dfs_visited[p] = true;
	return dfs(p, k + 1, l + minlen);
}

void get_down()
{
	down = 0;
	for (int i = 1; i <= n; i++)
	{
		int temp[MAXLENGTH];
		for (int j = 1; j <= n; j++)
		{
			temp[j] = value[i][j];
		}
		sort(temp + 1,temp + n + 1);
		down = down + temp[1] + temp[2];
	}
	down /= 2;
}

int get_lb(Node p)
{
	int ret = p.sumv * 2;
	int min1 = 999, min2 = 999;
	for (int i = 1; i <= n; i++)
	{
		if (!p.visited[i] && min1 > value[p.start][i])min1 = value[p.start][i];
	}
	ret += min1;
	for (int i = 1; i <= n; i++)
	{
		if (!p.visited[i] && min2 > value[p.end][i])
		{
			min2 = value[p.end][i];
		}
	}
	ret += min2;
	for (int i = 1; i <= n; i++)
	{
		if (!p.visited[i])
		{
			min1 = min2 = 999;
			for (int j = 1; j <= n; j++)
			{
				if (min1 > value[i][j])
					min1 = value [i][j];
			}
			for (int j = 1; j <= n; j++)
			{
				if (min2>value[i][j] && value[i][j]>min1)
				{
					min2 = value[i][j];
				}
			}
			ret += min1 + min2;
		}
	}
	return ret / 2;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值