Travelling HDU - 3001(三进制状态压缩) 题解


题目: After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn’t want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help. Input There are several test cases,the first line is two intergers n(1<=n<=10) and m,which means he needs to visit n cities and there are m roads he can choose,then m lines follow,each line will include three intergers a,b and c(1<=a,b<=n),means there is a road between a and b and the cost is of course c.Input to the End Of File. Output Output the minimum fee that he should pay,or -1 if he can’t find such a route. 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 90 7

题目大意就是可以免费从任意点出发,每个点最多只能取两次,求走完所有点得最少消耗。 本来如果每个点只能去一次的话,用二进制串转十进制来保存就可以很轻松处理出来,用dp[i][j]表示走过的得集合得十进制状态表示为i,终点为j的最小消耗,但是因为每个点可以到达两次,状态压缩就得变成三进制了,状态x的三进制第i位为1时表示该点到达过一次,2表示到达过两次。终末状态也变成了从每一个点都到过一次到每个点都到过两次之间的所有状态。 首先使用了一个数组f[i][j]来表示三进制状态压缩为十进制时为i的情况下,j点去过的次数。three[i]表示三进制下第i为1时的十进制表示,对于一个状态i,不断的%3再/3就可以处理出改状态下j点的次数。这样便可以预处理出所有状态下任意点到达的次数了,当我们在递推dp时,若要从状态dp[i][j]到达k,若f[i][k]==2的话说明k点已经去过两次了不能再去了。 题目没说给出的图为简单图,所以会有重边,只需储存的时候存最小的就行了。

#include<iostream> #include<string.h> #include<algorithm> #include<string> #include<stdio.h> using namespace std; int m,n; int map[13][13]; int f[60000][13]; int dp[60000][13]; int three[12]; void init(){ three[1]=1; for(int i=2;i<=11;i++) three[i]=three[i-1]*3; for(int i=0;i<three[11];i++) { int tmp=i; for(int j=1;j<=10;j++) { f[i][j]=tmp%3; tmp/=3; } } } int main(){ int x,y,z; init(); while(~scanf("%d%d",&n,&m)){ memset(map,0x7f,sizeof(map)); for(int i=0;i<m;i++){ scanf("%d%d%d",&x,&y,&z); map[x][y]=min(map[x][y],z); map[y][x]=min(map[y][x],z); } memset(dp,0x7f,sizeof(dp)); int ans=dp[0][0]; int inf=ans; for(int i=1;i<=n;i++){ dp[three[i]][i]=0; } int upper=three[n+1]-1; for(int i=1;i<=upper;i++){ bool ok=1; for(int j=1;j<=n;j++){ if(f[i][j]==0) ok=0; if(dp[i][j]==inf)continue; for(int k=1;k<=n;k++){ if(j==k)continue; if(map[j][k]!=inf&&f[i][k]!=2){ int newp=(i+three[k]); dp[newp][k]=min(dp[newp][k],dp[i][j]+map[j][k]); } } } if(ok){ for(int j=1;j<=n;j++){ ans=min(ans,dp[i][j]); } } } if(ans==inf){cout<<-1<<endl;} else cout<<ans<<endl; } return 0; }