题意:旅行商问题,特点是一个点最多经过两次。
题解:和只能经过一次的旅行商一样使用状态压缩,但是不用二进制而是用三进制表示。
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int f3[11]; void pow3()//3的次幂打表 { int i; f3[0]=1; for(i=1;i<11;i++)f3[i]=3*f3[i-1]; } int dp[11][60000],mp[11][11],minn,bit3[11],n;//dp[i][j]记录三进制数j以i结尾时的最小花费 int turnp(int bit3[],int state)//解压缩,求状态下经历的点数 { int i; int cnt=0; for(i=0;i<n;i++) { bit3[i]=state%3; state/=3; if(bit3[i])cnt++; } return cnt; } void init()//初始化 { int i,j; for(i=0;i<n;i++){ for(j=0;j<n;j++)mp[i][j]=-1; for(j=0;j<f3[n];j++)dp[i][j]=-1; } } int main() { int m,a,b,c,i,k,j; pow3(); while(~scanf("%d%d",&n,&m)){ init(); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&c); a--,b--; if(mp[a][b]==-1)mp[a][b]=mp[b][a]=c; else mp[a][b]=mp[b][a]=min(mp[b][a],c); } minn=-1; int now; for(now=0;now<f3[n];now++)//先从状态遍历 { k=turnp(bit3,now); for(i=0;i<n;i++) { if(bit3[i]) { if(k==1)dp[i][now]=0;//只经历了一个点那就时以此为起点的刚开始的状态 if(dp[i][now]==-1)continue;//在之前没被遍历到,说明i在这种状态下不能作为结尾 if(k==n)//遍历所有点 { if(minn==-1)minn=dp[i][now]; else minn=min(minn,dp[i][now]); } for(j=0;j<n;j++)//状态转移部分 { if(j!=i&&mp[i][j]!=-1&&bit3[j]<2) { int next=now+f3[j]; if(dp[j][next]==-1)dp[j][next]=dp[i][now]+mp[i][j]; else dp[j][next]=min(dp[j][next],dp[i][now]+mp[i][j]); } } } } } printf("%d\n",minn); } return 0; }