【数据结构•并查集】旅行(haoi2006,河南省选第一试第2题)

题目描述

  z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。z小镇附近共有n个景点(编号为1,2,3,…,n),这些景点被m条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,z小镇有个奇怪的规定,就是对于一条给定的公路ri,任何在该公路上行驶的车辆速度必须为vi。速度变化太快使得游客们很不舒服,因此从一个景点前往另一个景点的时候,大家都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。

输入输出格式

输入格式:

  输入文件(comf.in)
  第一行包含两个正整数,n和m。
  接下来的m行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。
  最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

输出格式:

  输出文件(comf.out)
  如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。

输入输出样例

输入样例#1:

4 2
1 2 1
3 4 2
1 4

输出样例#1:

IMPOSSIBLE

提示信息

样例二:
sample input
3 3
1 2 10
1 2 5
2 3 8
1 3

sample output
5/4

样例三:
sample input
3 2
1 2 2
2 3 4
1 3

sample output
2

数据范围:
  1 < n ≤ 500;1 ≤ x,y ≤ n,0 < v < 30000,x≠y;0 < m ≤ 5000。
解题提示:
  按速度由大到小排序,把依次加入边。

算法分析:

提示说得真好,其实就是一个暴力——可能有点像最小生成树的克鲁斯卡尔算法:

将边按权值由大到小排序

两层循环:

第一层枚举所有边:

将该边作为图中最大边

第二层循环由前往后依次加入新边,若s与t联通,则为一组可行解,存入;

最后算可行解比值,按题意,输出比值最小的分子与分母(记得约分)

上代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct Edge{
	int u;
	int v;
	int w;
}edge[10010];
int cnt=0,st,ed,f[510];
int jie[5001][2],jjie;
double mi=10000000;
int maxn,minn;
int cmp(Edge x,Edge y)
{
	return x.w<y.w;
}
int find(int k)
{
	if(f[k]==k) return k;
	else return f[k]=find(f[k]);
}
int gcd(int x,int y)
{
	if(y==0) return x;
	return gcd(y,x%y);
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		edge[++cnt]=(Edge){x,y,z};
	}
	cin>>st>>ed;
	sort(edge+1,edge+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++) f[j]=j;
		for(int j=i;j>=1;j--)
		{
			int u=edge[j].u;
			int v=edge[j].v;
			
			if(find(u)!=find(v)) f[find(u)]=find(v);
			if(find(st)==find(ed))
			{
				jie[++jjie][0]=edge[i].w;
				jie[jjie][1]=edge[j].w;
				break;
			}
		}
	}
	for(int i=1;i<=jjie;i++)
	{
		double val=double(jie[i][0])/jie[i][1];
		if(mi>val)
		{
			mi=val;
			maxn=jie[i][0];
			minn=jie[i][1];
		}
	}
	if(jjie==0)
	{
		cout<<"IMPOSSIBLE";
		return 0;
	}
	if(maxn%minn==0)
	{
		cout<<maxn/minn;
	}
	else
	{
		int cnt=2;
		while(gcd(maxn,minn)!=1)
		{
			if(maxn%cnt==0&&minn%cnt==0)
			{
				maxn/=cnt;
				minn/=cnt;
			}
			cnt++;
		}
		cout<<maxn<<'/'<<minn;
	}
	return 0;
}  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值