题意:给出一个无向图,每个点不能被经过超过两次,选择一个起点问经过所有点至少一次的最短路径。
解法:注意此题是每个点不能经过超过两次,这和一般的TSP问题不同。但是也没有使得此题变得很复杂,原来的状态我们是用二进制压缩来表示,这时候就不够用了,我们得表示经过0次,1次,2次。想到什么了?没错就是三进制表示即可,其他和TSP问题差不多。dp[S][x]代表经过状态为S现在在x点距离完成目标的最短路径长度。照例dp即可。
#include<bits/stdc++.h> using namespace std; const int M=10*10; const int INF=0x3f3f3f3f; int n,m; int cnt,head[M<<1],nxt[M<<1],to[M<<1],len[M<<1]; void add_edge(int x,int y,int z) { nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt; } int inc(int S,int x) { int p=1; for (;x>1;x--) p*=3; return S+p; } int get(int S,int x) { int t=1; while (t<=n) { if (t==x) return S%3; S/=3; t++; } } bool check(int S) { int t=1; while (t<=n) { if (S%3==0) return 0; S/=3; t++; } return 1; } int dp[200000][12]; int dfs(int S,int x) { if (check(S)) return 0; if (dp[S][x]!=-1) return dp[S][x]; int ret=INF; for (int i=head[x];i;i=nxt[i]) { int y=to[i]; if (get(S,y)<2) ret=min(ret,dfs(inc(S,y),y)+len[i]); } return dp[S][x]=ret; } int main() { while (scanf("%d%d",&n,&m)==2) { cnt=1,memset(head,0,sizeof(head)); for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add_edge(x,y,z); add_edge(y,x,z); } memset(dp,-1,sizeof(dp)); int ans=INF; for (int i=1;i<=n;i++) ans=min(ans,dfs(inc(0,i),i)); if (ans==INF) puts("-1"); else printf("%d ",ans); } return 0; }