Description
Input
Output
Sample Input
2 1 1 2 100 3 2 1 2 40 2 3 50 3 3 1 2 3 1 3 4 2 3 10
Sample Output
100 907
n(n<=10)个城市,m条道路,可以从任意一个城市出发,然后遍历所有城市,每个城市最多经过2次,问最短路程是多少
这个问题和旅行商问题类似,但是不同之处在于每个城市最多可以走2次,旅行商问题每个城市只走1次,然后回到出发点,一般都是从0点出发回到0点;在旅行商问题中用二进制表示该城市走过没走过得状态,在本题中需要用三进制表示走过的状态;
具体见代码
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <string> #include <stdlib.h> using namespace std; const int inf = 0x3f3f3f3f; int state[12]= {0,1,3,9,27,81,243,729,2187,6561,19683,59049}; ///几个直达状态 int mp[12][12]; int dp[12][60000];///3^10<60000 ///3进制表示,dp[i][j]表示到i点的j状态 每个点状态有0(没走过)1(走了一次)2(走了两次) int visit[12][60000]; int m,n; int min(int a,int b) { if(a<b) return a; return b; } void init()///visit[i][j]表示j状态到达i点几次 { memset(visit,0,sizeof(visit)); for(int i=0; i<60000; i++ { int t=i; for(int j=1; j<=10; j++) { visit[j][i]=t%3; t/=3; if (!t) break; }///类似与二进制状态的初始化,只不过这里不能用位运算 } } void solve() { int ans=inf; for(int i=0; i<=n; i++) dp[i][state[i]]=0; for(int i=0; i<state[n+1]; i++)///所有点仅经过这个点且只经过一次的 { bool f=1; for(int j=1 ; j<=n; j++) { if (!visit[j][i]) f=0; if (dp[j][i]==inf) continue; for(int k=1; k<=n; k++) { if (k==j || mp[j][k]==inf || visit[k][i]>=2) continue; dp[k][i+state[k]]=min(dp[j][i]+mp[j][k],dp[k][i+state[k]]); ///k行(state[k]表示只经过k状态与i状态(原状态)之和) ///表示j点i状态通过的方法更新至k点[i+state[k]]状态的方法 } } if (f) { for(int j=1; j<=n; j++) { ans=min(ans,dp[j][i]);///更新最小值 } } } if (ans==inf) ans=-1;///无法完成 printf("%d\n",ans); } int main() { while(~scanf("%d%d",&n,&m)) { init(); memset(dp,inf ,sizeof(dp)); memset(mp,inf ,sizeof(mp)); for(int i=0; i<m; i++) { int u,v,num; scanf("%d%d%d",&u,&v,&num); mp[u][v]=mp[v][u]=min(mp[u][v],num); } solve(); } return 0; }