题目意思很明显,但是和平时的TSP问题有一点点区别,平时的TSP问题,每个点只可以走一次,然后回到我们的起点,但是这个是每个点至多走两次,并且起点是任意的,问题是最短的距离是多少?
这里参考了其他的博客,得知用到了三进制,因为每个点最多可以走两次,所以每个点的状态可以包含0、1、2三种,那么这里位运算就不可以使用了,我们就在程序的开始进行了打表预,处理将每个数的三进制中第i为上的数字都保存在数组digit中,然后就是判断每个点都走过的情况下是暴力每个数都没有0的存在,剩下的就是状态的转移了:
dp[state][i]:状态state下,当前所在的点为i走过的最短的距离
dp[now_state][i] = min(dp[now_state][i] , dp[state][j] + dis[j][i] )
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <algorithm> #define INF 0x3f3f3f3f #define LL long long using namespace std; const int N = 12; const int MAXN = 60000; int n,m; int dis[N][N]; int dp[MAXN][N]; int three[N],digit[MAXN][N]; void Init() {//预处理 three[0] = 1; for(int i = 1;i <= 10;i ++) three[i] = three[i-1] * 3;//3^i for(int i = 0;i < three[10];i ++) { int temp = i; for(int j = 0;j < 10;j ++) { digit[i][j] = temp % 3; temp /= 3; } } } int main() { Init(); while(~scanf("%d%d",&n,&m)) { memset(dis,INF,sizeof(dis)); memset(dp,INF,sizeof(dp)); int ans = INF; int a,b,c; for(int i = 0;i < m;i ++) { scanf("%d%d%d",&a,&b,&c); if(dis[a-1][b-1] > c) dis[a-1][b-1] = dis[b-1][a-1] = c; } for(int i = 0;i < n;i ++) dp[three[i]][i] = 0; for(int S = 0;S < three[n]; S ++)//枚举所有的状态 { bool flag = true;//判断所有的点是否都不为0,不为0,则falg = true,含有0,falg = false; for(int i = 0;i < n;i ++) { if(digit[S][i] == 0) flag = false; if(dp[S][i] == INF) continue; for(int j = 0;j < n;j ++) { if(dis[i][j] != INF && digit[S][j] < 2)//更新状态 dp[S+three[j]][j] = min(dp[S+three[j]][j] , dp[S][i] + dis[i][j]); } } if(flag) { for(int i = 0;i < n;i ++) ans = min(ans,dp[S][i]); } } if(ans == INF) printf("-1\n"); else printf("%d\n",ans); } return 0; }