首先,我们先来认识一下什么叫做TSP问题
旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。假设这个n很小,我们就可以使用状态压缩的方法求解,在一般的TSP问题中的用状压求解的题目,我们可以定义一个dp数组,dp[i][v],其中v表示一个集合,dp[i][v]表示到i这个点经过v中所有点的最小路径.
假设我们从s出发,最后再回到s
1.那么最开始,只有dp[s][{s}]=0,其余均等于inf
2.其他情况下,dp[i][state]=min(dp[i][state],dp[j][state‘]+c[j][i])
3.最后我们的结果,ans=min(ans,dp[i][state]+c[i][s]),因为我们要求的是一个环的最短路,所以还要加上回来的距离
那么还有一个问题,我们要如何存下这个集合,当然是用状态压缩的方法,s|1<<(k),表示由原来的状态s转移到加上k这个点的状态,那么就很好求解了对吧
题目大意:多组数据,给定n,一个起点0,以及这n+1个点之间的距离,求从起点出发经过每个点一次,再回到起点的最短距离.注意到n<=10,我们可以使用状压dp来做
思路:首先先预处理出这n+1个点之间的最短距离,因为n很小,我们可以使用floyed来处理.然后就是套我上面的说的三种情况,具体可以代码中的注解
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<string> 5 #include<cstring> 6 #define in(i) (i=read()) 7 using namespace std; 8 const int inf=0x3f3f3f; 9 int read() 10 { 11 int ans=0,f=1; 12 char i=getchar(); 13 while(i<‘0‘||i>‘9‘) 14 { 15 if(i==‘-‘) f=-1; 16 i=getchar(); 17 } 18 while(i>=‘0‘&&i<=‘9‘) 19 { 20 ans=(ans<<1)+(ans<<3)+i-‘0‘; 21 i=getchar(); 22 } 23 return ans*f; 24 } 25 int n; 26 int dp[13][1<<13]; 27 int mp[13][13]; 28 void floyed() 29 { 30 for(int k=1;k<=n;k++) 31 for(int i=1;i<=n;i++) 32 for(int j=1;j<=n;j++) 33 mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]); 34 return; 35 } 36 int main() 37 { 38 while(1) 39 { 40 int ans=inf; 41 in(n); 42 if(!n) break; 43 n++; 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=n;j++) 46 in(mp[i][j]);//输入每两个点之间的距离 47 floyed();//求出n+1个点两两之间的最短距离 48 memset(dp,inf,sizeof(dp)); 49 dp[1][1]=0;//默认以1为起点,集合内最开始状态为1<<(1-1)=1,所以dp[1][1]=0 50 for(int i=1;i<(1<<n);i++)//枚举状态 51 { 52 for(int j=1;j<=n;j++)//枚举每个点 53 { 54 if((i&(1<<(j-1)))!=0)//判断这个是否在集合中 55 { 56 for(int k=1;k<=n;k++)//如果不在就以它为中转点转移 57 { 58 if(!(i&(1<<(k-1)))) 59 { 60 dp[k][i|(1<<(k-1))]=min(dp[k][i|(1<<(k-1))],dp[j][i]+mp[j][k]);//状态转移方程 61 } 62 } 63 } 64 } 65 } 66 for(int i=2;i<=n;i++) 67 ans=min(ans,dp[i][(1<<n)-1]+mp[i][1]);//还要回来才是一个环,因此还要加上到起点的距离 68 cout<<ans<<endl; 69 } 70 }