hdu 3001 Travelling(状态压缩 三进制)

Travelling

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6600    Accepted Submission(s): 2144


Problem Description
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
 

 

Source
 
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#define M 25
#define INF 0x1f1f1f1f
#define N 65000
using namespace std;
int n,m;
int state[M]={0,1,3,9,27,81,243,729,2187,6561,19683,59049};//state[i]表示第i个点城市状态用3进制表示是多少
int dis[N][M];//dis[i][j]表示i状态j点走过几次
int dp[N][M];//dp[i][j]表示第i个状态最后到达j的最小距离
int g[M][M];//表示两点间的距离
/*
寻找最短路径用了floyd算法的原理,求两个点之前的最短距离,不断寻找中间点来缩小权值;
状态转移:遍历所有的三进制状态,找到上一个状态(第j个城市没去过的或者去过一次的)然后转移到这个状态j位置上加一的状态上,但是能不能转移
要看本次转移是不是会缩短时间
*/
int main()
{
    //freopen("in.txt","r",stdin);
	for(int i=0;i<59050;i++)
	{
		int x=i;
		for(int j=1;j<=10;j++)//最多有十位
		{
			dis[i][j]=x%3;
			x/=3;
			if(x==0)
				break;
			//cout<<j<<endl;
		}
		//cout<<i<<endl;
	}//初始化第i个状态的j位上的数是几
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		int a,b,c;
		memset(g,INF,sizeof g);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			if(c<g[a][b])
				g[a][b]=g[b][a]=c;
		}//处理输入
		memset(dp,INF,sizeof dp);
		int tol=pow(3.0,n);
		for(int i=1;i<=n;i++) dp[state[i]][i]=0;//初始化从最后到达i城市最小距离的这个状态转移到i城市需要的最小时间就是0;
		int cur=INF;
		//下面求最短路径的原理是floyd算法(两个点之间不断寻找中间点来缩小权值)
		for(int i=0;i<tol;i++)//枚举所有可能的状态
		{
			int flag=1;;//用于判断当前状态是不是没走
			for(int j=1;j<=n;j++)//枚举你需要找的中间过程的城市
			{
				if(dis[i][j]==0) flag=0;//判断这个是不是
				if(dp[i][j]==INF) continue;
					//cout<<"dis[i][j]="<<dis[i][j]<<endl;
				for(int k=1;k<=n;k++)//枚举当前当前状态(就是你要转移到的状态)
				{
					if(j==k) continue;
					// cout<<"come in"<<endl;
					if(g[j][k]!=INF&&dis[i][k]<2)//从j到k的道路是通的,并且在这个状态中k点经过的次数不到两次
					{
						int newstate=i+state[k];//新的状态就是走到这个城市,给k点经过的次数加一
						dp[newstate][k]=min(dp[newstate][k],dp[i][j]+g[j][k]);
					}
					
					// cout<<"dp[newstate][k]="<<dp[newstate][k]<<endl;
				}
				//cout<<j<<endl;
			}
			if(flag)
				for(int j=1;j<=n;j++)
					cur=min(dp[i][j],cur);
		}
		if(cur==INF)//如果取到正无穷就是那么没有联通路所以就不可能走完
			puts("-1");
		else
			printf("%d\n",cur);
	}
   return 0;
}

  

posted @ 2016-08-06 15:28  勿忘初心0924  阅读(253)  评论(0编辑  收藏  举报