[HAOI2006] 旅行

题目

题目描述
Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。Z小镇附近共有N个景点(编号为1,2,3,…,N),这些景点被M条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。速度变化太快使得游客们很不舒服,因此从一个景点前往另一个景点的时候,大家都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。
输入输出格式
输入格式:
第一行包含两个正整数,N和M。
接下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。
最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。
输出格式:
如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。
输入输出样例
输入样例#1:
4 2
1 2 1
3 4 2
1 4
输出样例#1:
IMPOSSIBLE
输入样例#2:
3 3
1 2 10
1 2 5
2 3 8
1 3
输出样例#2:
5/4
输入样例#3:
3 2
1 2 2
2 3 4
1 3
输出样例#3:
2
说明
【数据范围】
1<N≤500
1≤x,y≤N,0<v<30000,x≠y
0<M≤5000

题解

  • 这个题是让我们找一条 s , t s,t s,t的路径 ,使得这条路上的最大边和最小边的比值最小
  • 对于 s , t s,t st是否连通我们可以并查集来判断
  • 那我们可以先固定一条最大边,然后不断向其中添加小边直到 s , t s,t st联通,这个同样用并查集来维护。
  • s , t s,t s,t一旦联通,此时的最大边和最小边就是一组可能的解
  • 最后我们枚举所有可行解,然后将他们比较一下就得出最优解
  • 而对于最终答案要是分数的形式,我们可以采用 g c d gcd gcd化简。

code


#include <bits/stdc++.h>
const int maxn = 1e4 + 100;
const int maxm = 1e7 + 100;
const int inf = 0x3f3f3f3f;
typedef long long LL;
using namespace std;

template <typename T>
inline void read(T &s) {
    s = 0;
    T w = 1, ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
    while (isdigit(ch))  { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
    s *= w;
}

template<typename T>
inline void write(T s) {
	if (s < 0) putchar('-'), s = -s;
	if (s > 9) write(s / 10);
	putchar(s % 10 + '0');
}

int n;
int m;
int st;
int ed;
int cnt;
int fa[maxn];
int vis[maxn];
struct node { int from, to, dis; } t[maxn];
struct anss { int maxnum, minnum; } a[maxm];

inline bool cmp(node aa, node bb) {
	return aa.dis < bb.dis;
}

inline int gets(int k) {
	return fa[k] == k ? k : fa[k] = gets(fa[k]);
}

inline void init() {
	for (int i = 1; i <= n; ++i) 
		fa[i] = i;
}

inline int gcd(int x, int y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}

int main() {
	read(n), read(m);
	for (int i = 1; i <= m; ++i) {
		read(t[i].from), read(t[i].to), read(t[i].dis);
	}
	read(st), read(ed);
	
	init();
	memset(vis, 0, sizeof(vis));
	
	for (int i = 1; i <= m; ++i) {
		int u = gets(t[i].from), v = gets(t[i].to);
		if (fa[u] != v) 	
			fa[u] = v;
	}
	if (gets(st) != gets(ed)) {
		printf("IMPOSSIBLE\n");
		exit(0);
	}
	
	sort(t + 1, t + m + 1, cmp);
	int fs = gets(st);
	for (int i = 1; i <= n; ++i) {
		if (gets(i) != fs) vis[i] = 1;
	}
	
	for (int i = 1; i <= m; ++i) {
		if (vis[ t[i].from ] || vis[ t[i].to ]) continue;
		init();
		for (int j = i; j >= 1; j--) {
			int u = t[j].from, v = t[j].to;
			if (vis[u] || vis[v]) continue;
			int fu = gets(u), fv = gets(v);
			if (fu != fv) {
				fa[fu] = fv;
				int fs = gets(st), fe = gets(ed);
				if (fs == fe) {
					a[++cnt].maxnum = t[i].dis;
					a[cnt].minnum = t[j].dis;
				}
			}
		}	
	}
	
	int ansnum;
	double res = 999999.99;
	for (int i = 1; i <= cnt; ++i) {
		if ((double)a[i].maxnum / (double)a[i].minnum < res) {
			res = (double)a[i].maxnum / (double)a[i].minnum;
			ansnum = i;
		}
	}
	
	if (a[ansnum].maxnum % a[ansnum].minnum == 0) {
		write(a[ansnum].maxnum / a[ansnum].minnum);
		puts("");
	}
	else {
		int g = gcd(a[ansnum].maxnum, a[ansnum].minnum);
		a[ansnum].maxnum /= g;
		a[ansnum].minnum /= g;
		write(a[ansnum].maxnum), putchar('/'), write(a[ansnum].minnum);
		puts(""); 
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值