推销员问题

  旅行商问题,即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");
     } 
} 

原文地址:https://www.cnblogs.com/mengxiaoleng/p/11166886.html