售货员的难题

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\n",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 }


 

 

转载于:https://www.cnblogs.com/ZhengZi-qiang/p/4523909.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值