实验名称
分支限界法求解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;
}