nyoj932-旅游【欧拉图+添边】

旅游

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述
飘谊到了S国旅游,S国有n个城市,城市之间有许多观光道路,为了看到最多的风景,飘谊希望从某个城市出发,走完所有的观光道路,并且回到出发点。飘谊想知道他走完观光道路并回到源点的最短路径
输入
输入包含多组测试数据。
每组输入的第一行为2个正整数n和m(n<=15,m<1000),n表示城市的个数,m表示观光道路的个数。接下来m行,每行输入3个正整数u,e,l(1<=u,e<=n,1<=l<=5000),表示标号为u和e的城市由一条路相连,长度为l。任意两个城市之间可能会由多条路相连而且路的长度也可能会相等。每条路都是双向可通行的。任意两个城市是可以相互到达的。
输出
对于每组输入,输出最短路。
样例输入
4 5
1 2 3
2 3 4
3 4 5
1 4 10
1 3 12
样例输出
41


解法思想简单来说就是一条观光道路能走一次就不走第二次 。建立无向图并在里面寻找一个子图,这个子图要求是个欧拉图且权值总和在所有欧拉子图中最大,就称这个子图为G吧。走完所有观光道路且路径最短:在G中的道路就走一次,不在G中的道路走两次。
G图是欧拉图,所以G图的路径就是只走一次的路径。不在G图的边可以想象是“挂”在G图上的,要保证G图的一笔画性,就需要走两次(一出一回)来处理不在G图的边。
因为G图是原图的子图。要寻找它需要删边,这点很不好处理。
可以分析知题目要求的最短路的路径,就是一个欧拉图。所以定义无向图的所有边只能走一次。走两次的道路就有两条边了(一条建图是本来就有,另一条就添进去)。这样最短路就可以在原图的基础上添边构成了。

添的边表示这条路走了两次。对于怎么添边:为了构成一个欧拉图的,就得要消除图中的奇度顶点,要么两个奇度顶点之间连,要么两个奇度顶点连在同一个偶度顶点上(这两个条件可以用floyed算法处理)。用二进制0或1,来表示这个奇度点是否解决。搜索枚举所有的奇度顶点两两组合的情况。枚举的最小值加上原图的权值就是最短路的值。

 
#include <stdio.h>
#include <string.h>
#define Maxsize 20
#define FindMin(a,b) a>b?b:a
#define INF 999999999
int g[Maxsize][Maxsize];
int degree[Maxsize];
int count;
int v[Maxsize];
int ans;
int T;
void dfs(int u,int value)
{
	int i,j;
	if(u==T)
	{
		ans=FindMin(value,ans);
//		printf("%d %d\n",value);
	}
	for(i=0;i<count;i++)
	{
		if(!((u>>i)&1))
		{
			for(j=i+1;j<count;j++)
				if(!((u>>j)&1))
				{
					int next=u;
					next=next|(1<<i);
					next=next|(1<<j);
					dfs(next,value+g[v[i]][v[j]]);
				}
		}
	}
}
int main()
{
	int n,m;
	int u,e,l;
	int i,j,k;
	int sum=0;
	while(~scanf("%d%d",&n,&m))
	{
		memset(degree,0,sizeof(degree));
		for(i=0;i<=n;i++)
			for(j=0;j<=n;j++)
				g[i][j]=INF;
		sum=0;
		while(m--)
		{
			scanf("%d%d%d",&u,&e,&l);
			sum+=l;
			degree[u]++;degree[e]++;
			g[u][e]=g[e][u]=FindMin(g[u][e],l);
		}
		for(k=1;k<=n;k++)
			for(i=1;i<=n;i++)
				for(j=1;j<=n;j++)
					g[i][j]=FindMin(g[i][j],g[i][k]+g[k][j]);
		count=0;
		for(i=1;i<=n;i++)
			if(degree[i]&1)
				v[count++]=i;
		T=0;
		for(i=0;i<count;i++)
		{
			T=(T<<1)|1;
		}
		ans=INF;
		dfs(0,0);
		
		printf("%d\n",sum+ans);
	}
	return 0;
}        


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值