推销员问题
旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
这是我大二期末数据结构课程设计的题目之一,其是关于图这种数据结构的。按照其题目大意可以大致理解为一个旅行商,要从自己原本的城市出发,走遍所有的城市最后回到自己原来的城市。在这个问题中的约束是每个城市都只能拜访一次。目的是求旅行商走一圈得到的最短路径的大小。
对于这个问题,普遍有两种思路。第一种思路是暴力破解法。
举个例子。

如图所示,一共有五个城市其彼此之间相互连通。旅行商要走完一圈回来且不重复,实际上走得路径的各种可能性也不过是各种城市的排列组合。
ABCDEA、ACBDEA、......等等,按照排列组合来计算共有4!种情况,在这些组合中选取一种距离最短的方式就好了。当城市数为5的时候看起来还好,但是这种暴力排列组合的时间复杂度是很大的,达到了O(n!),所以对于城市数多的情况下,是很难算出结果的。
第二种,就是贪心法。所谓贪心法,就是每一步都走最短的路径,且通往的城市是没有访问过的。这种方法,可以得到尽可能短的路径,但是也会出现一种问题,就是局部最优解不一定是整体最优解,你可能一味的贪心得到是这样的结果,之前走的路都是最短的,但是最后返回原来城市的路径究极长,显然就不满足最短路径了。我的方法就属于这种贪心法,废话不多说了,上代码。
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <iostream>
# define INF 100000
# define max 100
using namespace std;
//推销员问题
typedef struct Graph{
int distance[max][max];//城市与城市之间的距离
char place[max];
int city;//城市的个数
int edge;//边的个数
}CI,*PCI;//城市
bool visited[max];//判断有没有访问过该城市
int order[max];//访问节点的顺序
int num_vertax(void);//求已经收入顶点集合的顶点的个数
void init_graph(CI* G);//初始化城市
void find_bestway(CI*G);//找到最好的路径
int locate_graph(CI*G,char elem);//找到顶点的下标位置
void show_graph(CI* G);//把图打印出来
int main(void)
{
char elem;
PCI G=(PCI)malloc(sizeof(CI));
init_graph(G);
show_graph(G);
find_bestway(G);
free(G);
system("pause");
return 0;
}
void init_graph(CI* G)
{
int i,j;
printf("请输入一共有多少个城市:\n");
scanf("%d",&(G->city));
///初始化城市
for(i=0;i<G->city;i++)
{
for(j=0;j<G->city;j++)
{
if(i==j)
{
G->distance[i][j]=0;
}
else
{
G->distance[i][j]=INF;
}
}
}
printf("请输入城市的名称:\n");
for(i=0;i<G->city;i++)
{
cin>>G->place[i];
}
///输入城市与城市之间的距离
for(i=0;i<G->city;i++)
{
for(j=0;j<G->city;j++)
{
if(G->distance[j][i]!=0&&G->distance[j][i]!=INF)
{
G->distance[i][j]=G->distance[j][i];
}
else if(i!=j)
{
printf("请输入%c城市与%c城市之间的距离:\n",G->place[i],G->place[j]);
scanf("%d",&G->distance[i][j]);
}
}
}
}
void find_bestway(CI* G)
{///找到最好的路径并输出出来
char begin;//开始的城市
int start;//开始城市的下标
int end;//结束城市的下标
int remain;//记录下标
int count=1;//计数器
int cost=0;//花费
int next;
int min_cost=0;//总的最小花费
for(int i=0;i<G->city;i++)//初始化所有城市都没有访问过
{
visited[i]=false;
}
for(int i=0;i<max;i++)//顺序序号数组我们用-1初始化
{
order[i]=-1;
}
cout<<"请输入您想从哪个城市出发"<<endl;
cin>>begin;
start=locate_graph(G,begin);
cout<<"start="<<start<<endl;
//开始的那一座城市声明已经访问过了
remain=start;
cout<<"remain="<<remain<<endl;
visited[start]=true;
order[0]=start;//访问顺序的第一个位置装着该下标
while(num_vertax()<G->city)
{
// cout<<"有"<<num_vertax()<<"个顶点已经加入"<<endl;
cost=INF;//初始化最大长度
for(int i=0;i<G->city;i++)//在与它相邻的所有节点中
{
if(start!=i&&visited[i]==false&&G->distance[start][i]<cost)
{
// cout<<"start="<<start<<endl;
// cout<<"i="<<i<<endl;
// cout<<"distance="<<G->distance[start][i]<<endl;
cost=G->distance[start][i];
// cout<<"第"<<"i="<<i<<"时候"<<cost<<endl;
next=i;
}
}
//cout<<"start="<<start<<" ";
start=next;
visited[start]=true;//下一个开始的节点为访问过了
order[count]=start;
cout<<"cost="<<cost<<endl;
min_cost=min_cost+cost;
count++;
}
//cout<<"之前min_cost="<<min_cost<<endl;
order[count]=order[0];
//cout<<"最后一段路为:"<<G->distance[count-1][remain];
//cout<<"remain="<<remain<<endl;
//cout<<"最后节点下标为"<<order[count]<<endl;
// cout<<"count="<<count;
//cout<<"last="<<G->distance[count][remain];
min_cost=min_cost+G->distance[order[count-1]][remain];
cout<<"最佳访问顺序为:"<<endl;
for(int i=0;i<G->city+1;i++)
{
cout<<G->place[order[i]]<<" ";
}
cout<<endl;
cout<<"最短的距离为:"<<endl;
cout<<min_cost;
cout<<endl;
}
int locate_graph(CI* G,char elem)//返回节点所在的下标
{
int i;
for( i=0;i<G->city;i++)
{
if(elem==G->place[i])
{
return i;
}
}
}
int num_vertax(void)//可以知道顶点集合之中一共有多少个顶点
{
int count=0;
while(order[count]!=-1)
{
count++;
}
return count;
}
void show_graph(CI* G)//将图的内容打印出来
{
int i,j;
printf("图的内容为:\n");
for(i=0;i<G->city;i++)
{
printf("%c\t",G->place[i]);
}
printf("\n");
for(i=0;i<G->city;i++)
{
for(j=0;j<G->city;j++)
{
printf("%d\t",G->distance[i][j]);
}
printf("\n");
}
}



浙公网安备 33010602011771号