HDU3001-Travelling(三进制+状态压缩)

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

分析:

题意:
有n个城市和m条路,现在我们要遍历所有城市,问所走的最短路程是多少?(这道题有一个问题,那就是一个城市最多可以经过两次,这样就有点难了,但还是难不住大佬们,于是花了两个小时消化大佬的代码,一点一点写出了自己的,和大佬的差不多)。

解析:
首先,我们用三进制来表示每种情况,如:
三进制的121表示第一个城市去过一次了,第二个城市去过2次了,第三个城市去过了一次,通过三进制,我们很容易的表示每个城市的遍历情况,但是,我们是从哪个城市开始的,哪个城市结束的呢?对于这个问题,由于这里最多有10个城市,那么我们就把原来的一维变成二位,如:我们用数组dp[state][i]表示在state这种情况下,我们最后经历的哪个城市是i市的最短路程,最后我们只需要从遍历完所有城市的情况中找到最小值即可!

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define INF 0x3f3f3f3f

using namespace std;

int map[15][15];
int three[15];
int dp[N][15];
int list[N][15];

int main()
{
	int n,m,a,b,len;
	three[0]=1;
	for(int i=1;i<=11;i++)
	{
		three[i]=three[i-1]*3;
	}
	for(int i=0;i<three[10];i++)
	{
		int val=i;
		for(int j=1;j<=10&&val;j++)
		{
			list[i][j]=val%3;
			val/=3;
		}
	}
	while(~scanf("%d%d",&n,&m))
	{
		memset(map,INF,sizeof(map));
		for(int i=0;i<three[n];i++)
		{
			for(int j=1;j<=n;j++)
			{
				dp[i][j]=INF;
			}
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%d",&a,&b,&len);
			map[b][a]=map[a][b]=min(map[a][b],len);
		}
		for(int i=1;i<=n;i++)
		{
			dp[three[i-1]][i]=0;
		}
		int sum=INF;
		for(int i=0;i<three[n];i++)
		{
			bool flag=true;
			for(int j=1;j<=n;j++)
			{
				if(!list[i][j])
					flag=false;
				if(dp[i][j]!=INF)
				{
					for(int k=1;k<=n;k++)
					{
						if(map[j][k]!=INF&&list[i][k]!=2)
						{
							dp[i+three[k-1]][k]=min(dp[i][j]+map[j][k],dp[i+three[k-1]][k]);
						}
					}
				}
			}
			if(flag)
			{
				for(int j=1;j<=n;j++)
				{
					sum=min(sum,dp[i][j]);
				}
			}
		}
		if(sum>=INF)
			printf("-1\n");
		else
			printf("%d\n",sum);
	}
	return 0;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值