每个顶点最多可访问两次的TSP问题。
对于每个顶点,用一个三进制位表示其访问次数,则n个顶点的状态为n位3进制数。
设dp[i][j]表示在状态i下到达顶点j的最小花费。
有状态转移方程:
dp[i+3j−1][j]=min(dp[i+3j−1][j],dp[i][k]+dis[k][j])
其中dis[k][j]为顶点k到j的距离。
说明:
- 初始化时,将dp初始化为INF(无穷大),dp[3^(i-1)][i]初始化为0,即从顶点i出发回到i的最小花费为0。
- 由状态转移方程知,转移是从状态i转移到状态i+3^(j-1)。在枚举状态的时候,需要考虑状态i是否合法,即第j位上的数是否小于2,若大于等于2,此时是不能转移的。
- 对于最终的结果,根据状态的定义,取dp[3^n-1][i]的最小值即为答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 11
typedef __int64 LL;
#define inf 0x3f3f3f3f
int D[maxn][maxn];
int dp[60000][maxn];
int getnum(int s,int k)
{
int cnt=1;
while(s){
int tmp=s%3;
if(cnt==k) return tmp;
s/=3;
++cnt;
}
return 0;
}
int a[11];
int main()
{
int n,i,j,k,m;
a[0]=1;
for(i=1;i<=10;++i) a[i]=3*a[i-1];
while(~scanf("%d%d",&n,&m))
{
memset(D,inf,sizeof(D));
for(i=1;i<=n;++i) D[i][i]=0;
for(i=1;i<=m;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
D[x][y]=D[y][x]=min(D[x][y],z);
}
memset(dp,inf,sizeof(dp));
for(i=1;i<=n;++i) dp[a[i-1]][i]=0;
for(i=1;i<a[n];++i)
{
for(j=1;j<=n;++j)
{
if(dp[i][j]==inf) continue;
for(k=1;k<=n;++k)
{
if(D[j][k]==inf||getnum(i,k)>=2) continue;
dp[i+a[k-1]][k]=min(dp[i+a[k-1]][k],dp[i][j]+D[j][k]);
}
}
}
int ans=inf;
for(i=1;i<=n;++i) ans=min(ans,dp[a[n]-1][i]);
printf("%d\n",ans==inf?-1:ans);
}
return 0;
}