After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn't want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help.
2 1 1 2 100 3 2 1 2 40 2 3 50 3 3 1 2 3 1 3 4 2 3 10
100 907
1、二进制tsp问题求解
2、三进制tsp问题求解
3、模拟退火算法求tsp问题(还没学会)
首先tsp(旅行商问题)问题是指有一个旅行商要去n个城市,这n个城市之间有不同的距离,问要走遍这n个城市且每个城市只走一次的最短路径是多少(个人理解,非官方的说法)。
那上面是二进制tsp,因为每个城市只有走和不走两种状态,可以状压dp求解。关于三进制就是每个城市可以最多走2次,每个城市有三个状态。
求解tsp问题的算法有很多,比如:模拟退火,遗传算法,蚁群算法,粒子群算法,动态规划算法。
因为tsp问题是NP问题,除了动态规划算法其他的好像都是概率性质的算法,主要想学一下模拟退化算法。关于动态规划算法也比较好理解,很多文章讲的是递归dp然后优化,其实本质都是状态压缩的dp。
下面给出一个三进制的tsp问题的题目,也是用状态压缩做的,二进制同理。
/*题目描述:有n个城市,每个城市最多走2次,有m个语句描述边的距离,然后求经历过n个城市的最小的距离,不限制起点和终点*/
#include <cstring> #include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <queue> using namespace std; int mp[12][12],n,dp[12][60000]; int arr(int three[],int sum) { int k=0; for(int i=0; i<n; i++) { three[i]=sum%3; sum/=3; if(three[i])k++; } return k; } int main() { int m,a,b,c,in[12],MIN,three[12],k; in[0]=1; for(int i=1; i<=10; i++)in[i]=in[i-1]*3; while(~scanf("%d%d",&n,&m)) { for(int i=0; i<n; i++) { for(int j=0; j<in[n]; j++) { dp[i][j]=-1; } for(int j=0; j<n; j++) mp[i][j]=-1; } for(int i=0; i<m; i++) { scanf("%d%d%d",&a,&b,&c); a--; b--; if(mp[a][b]!=-1) mp[a][b]=mp[b][a]=min(c,mp[a][b]); else mp[a][b]=mp[b][a]=c; } MIN=-1; for(int go=1; go<in[n]; go++)///从初始状态往下推导,即从只有终点开始 ///能通过终点而找出的其他状态进行更新,如果不能到达的状态说明是不可达的了。 { k=arr(three,go); for(int i=0; i<n; i++)///枚举的是终点 { if(three[i]) { if(k==1)dp[i][go]=0; if(dp[i][go]==-1)continue;///当到达这个状态时还没有从终点推导过来,说明是不可达的 if(k==n) { if(MIN==-1)MIN=dp[i][go]; else MIN=min(MIN,dp[i][go]); } for(int j=0; j<n; j++)///从终点往前推导,再经过j一次 { if(i!=j&&three[j]<2&&mp[i][j]!=-1) { int tgo=go+in[j];///表示在三进制中在j这个位置上的数加上1,表示以j为结尾, ///也就是j又来过一次,通过i点 if(dp[j][tgo]==-1) dp[j][tgo]=dp[i][go]+mp[i][j]; else dp[j][tgo]=min(dp[i][go]+mp[i][j],dp[j][tgo]); } } } } } printf("%d\n",MIN); } return 0; }