• 售货员的难题


    Description

    某乡有n个村庄(1< n < 20),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0 < s < 1000)是已知的,且A村到B村与B村到A村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为 1,他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

    Input

    村庄数n和各村之间的路程(均是整数)。

    Output

    最短的路程

    Sample Input

    3 {村庄数}
    0 2 1 {村庄1到各村的路程}
    1 0 2 {村庄2到各村的路程}
    2 1 0 {村庄3到各村的路程}
     
    Sample Output
    3
     
     
    思路:如今有很多关于解TSP问题的算法,研究了几天,废寝忘食,发现TSP问题真的好难,主要是理解起来很容易,实现却很困难,时间复杂度为阶乘。
    常用的也是易懂的,最简单的就是回溯法,其他还有分支限界法,NP完全性理论,近似算法(费用函数具有三角不等式性质时,可设计一个近似算法,而对于一般情况下的旅行商问题则不可能设计出具有常数性能比的近似算法,除非P=NP,略),网上还有一些其他的算法,我个人比较推荐模拟退火。
     
    下面贴出最简单的回溯法
     1 #include <iostream>
     2 using namespace std;
     3 
     4 #define MAX_VALUE 0xFFFFFF
     5 int n;//定点数
     6 int x[25];//当前解
     7 int bestx[25];//当前最优解
     8 int bestc;//当前最优值
     9 int cc;//当前费用
    10 int a[25][25];//图的邻接矩阵
    11 
    12 void swap(int &a,int &b)
    13 {
    14     int tep = a;
    15     a=b;
    16     b=tep;
    17 }
    18 
    19 
    20 void backtrack(int i)
    21 {
    22     int j;
    23     if(i==n)
    24     {
    25         if(a[x[n-1]][x[n]]<MAX_VALUE&&a[x[n]][1]<MAX_VALUE&&(bestc==MAX_VALUE||cc+a[x[n-1]][x[n]]+a[x[n]][1]<bestc))
    26         {
    27             for(j=1;j<=n;j++)
    28                 bestx[j]=x[j];
    29             bestc=cc+a[x[n-1]][x[n]]+a[x[n]][1];
    30         }
    31     }
    32     else
    33     {
    34         for(j=i;j<=n;j++)
    35             //是否可进入x[j]的子树?
    36             if(a[x[i-1]][x[j]]<MAX_VALUE&&(bestc==MAX_VALUE||cc+a[x[i-1]][x[j]]<bestc))
    37             {
    38                 //搜索子树
    39                 swap(x[i],x[j]);
    40                 cc+=a[x[i-1]][x[i]];
    41                 backtrack(i+1);
    42                 cc-=a[x[i-1]][x[i]];
    43                 swap(x[i],x[j]);
    44             }
    45     }
    
    46 }
    47 
    48 int tsp()
    49 {
    50     int i;
    51     for(i=1;i<=n;i++)
    52         x[i]=i;
    53     bestc = MAX_VALUE;
    54     
    55     cc=0;
    56     //搜索x[2:n]的全排列
    57     backtrack(2);
    58     return bestc;
    59 }
    60 
    61 int main()
    62 {
    63     int i,j,min;
    64     cin>>n;
    65     for(i=1;i<=n;i++)
    66     {
    67         for(j=1;j<=n;j++)
    68             cin>>a[i][j];
    69     }
    70     min = tsp();
    71     cout<<min<<endl;
    72     return 0;
    73 }

    以下贴出模拟退火的代码【转自  模拟退火算法——解决售货员的难题

     1 /*模拟退火*/
     2 /*AC代码:0ms*/
     3 #include <iostream>
     4 #include <ctime>
     5 #include <cstdlib>
     6 using namespace std;
     7 const int MAX=41;
     8 const int RANN=1000;
     9 const int RUNN=50;
    10 const int INF=99999999;
    11 int map[MAX][MAX],rpath[RANN][MAX],min[RANN],N;
    12 void adjust(int x[],int rn)//rn为调整次数
    13 {
    14     int a,b;
    15     while(rn--)
    16     {
    17         a=rand()%(N-1)+1;//调整的位置
    18         b=rand()%(N-1)+1;
    19         swap(x[a],x[b]);
    20     }
    21 }
    22 void get_map()
    23 {
    24     int i,j;
    25     for(i=1;i<=N;i++)
    26         for(j=1;j<=N;j++)
    27             scanf("%d",&map[i][j]);
    28 }
    29 void get_rpath()//产生RANN组初始数列
    30 {
    31     int i,j;
    32     for(i=0;i<RANN;i++)
    33     {
    34         for(j=1;j<=N-1;j++)
    35             rpath[i][j]=j+1;
    36         adjust(rpath[i],N-1);//初始调整
    37         min[i]=INF;
    38     }
    39 }
    40 
    41 void swap(int &x,int &y)//交换x,y
    42 {
    43     int t=x;
    44     x=y;
    45     y=t;
    46 }
    47 int get_dis(int x[])//返回路径长度
    48 {
    49     int sum=0,i,p=1;
    50     for(i=1;i<=N-1;i++)
    51     {
    52         sum+=map[p][x[i]];
    53         p=x[i];
    54     }
    55     sum+=map[p][1];
    56     return sum;
    57 }
    58 void numcpy(int x[],int y[])
    59 {
    60     for(int i=1;i<=N-1;i++)
    61         x[i]=y[i]; 
    62 }
    63 void get_ans()
    64 {
    65     int i,j,t=N-1,temp,p[41];
    66     while(t--)//调整的范围递减
    67     {
    68         for(i=0;i<RANN;i++)//遍历RanN组数据
    69         {
    70             for(j=0;j<RUNN;j++)//对于每组做RunN次
    71             {
    72                 numcpy(p,rpath[i]);//因为每次都在原有的rpath[i]上改变
    73                 adjust(p,t);
    74                 temp=get_dis(p);
    75                 if(temp<min[i])
    76                 {
    77                     numcpy(rpath[i],p);//更新rpath[i];
    78                     min[i]=temp;
    79                 }
    80             }
    81         }
    82     }
    83     int ans=min[0];
    84     for(i=1;i<RANN;i++)
    85         if(min[i]<ans)
    86             ans=min[i];
    87         printf("%d
    ",ans);
    88 }
    89 int main ()
    90 {
    91     srand(time(0));//以时间为随机数种子产生随机数
    92     while(scanf("%d",&N)!=EOF)
    93     {
    94         get_map();
    95         get_rpath();
    96         get_ans();
    97     }
    98     return 0;
    99 }


     

  • 相关阅读:
    SVN更新的时候前面的子母的意思(A C D M G U R I)
    SQL总结(一)基本查询
    eclipse中如何打开工作空间里面已经有的项目
    java for循环的几种写法
    Eclipse自动生成作者、日期注释等功能设置
    linux任务计划及周期性任务计划
    进程管理工具使用
    Btrfs管理及应用
    LVM基本应用,扩展及缩减实现
    Linux-RAID
  • 原文地址:https://www.cnblogs.com/ZhengZi-qiang/p/4523909.html
Copyright © 2020-2023  润新知