BZOJ1050&&洛谷P2502 [HAOI2006]旅行

这道题一眼看过去,应该是最小生成树没错了,我们可以暴力的枚举一下选哪些边,期望得分40~50

正解是什么呢?我们将边按照边权排序,因为我们要在保证联通的条件下,使得costmax/costmin最小,所以我们要让最小边尽量大的情况下,使得最大边尽量小,如何实现呢?我们发现,因为边权数组已经排好序,所以权值是递增的,所以我们可以选择一些小边权的边不选,看是否还能连通,这样就可以实现在最小边扩大的同时,缩小比值

(这次的代码函数较多,为了更清晰,我开了好多函数,不要喷我的码风,自认为还是比较好看的)

代码

//By AcerMo
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=5500;
int up,down;
int n,fa[M],siz[M],m;
double ans=3005000.0;
struct edge
{
	int fr,to,cost;
}priq[M<<2];
inline int find(int x)
{
	if (x!=fa[x]) return fa[x]=find(fa[x]);
	return x;
}
inline int gcd(int a,int b)
{
	if (!b) return a;
	return gcd(b,a%b);
}
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void unionn(int a,int b)
{
	if (siz[a]<siz[b]) siz[b]+=siz[a],fa[a]=b;
	else siz[a]+=siz[b],fa[b]=a;
	return ;
}
inline bool cmp(edge a,edge b){return a.cost<b.cost;}
inline void constt()
{
	for (int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
	return ;
}
inline void jud(int i,int k)
{
	double now=(double)priq[i].cost/priq[k].cost;
	if (ans>now) ans=now,up=priq[i].cost,down=priq[k].cost;
	return ;
}
int main()
{
	n=read();m=read();constt();
	for (int i=1;i<=m;i++)
	{
		priq[i].fr=read();priq[i].to=read();priq[i].cost=read();
		int r1=find(priq[i].fr),r2=find(priq[i].to);
		if (r1!=r2) unionn(r1,r2);	
	}
	int st=read(),ed=read();
	if (find(st)!=find(ed)) return puts("IMPOSSIBLE"),0;
	sort(priq+1,priq+m+1,cmp);
	for (int i=1;i<=m;i++)
	{
		bool flag=0;int e;constt();
		for (int k=i;k<=m;k++)
		{
			int r1=find(priq[k].fr);
			int r2=find(priq[k].to);
			if (r1!=r2) unionn(r1,r2);
			if (find(st)==find(ed)) {e=k;flag=1;break;}
		}
		if (flag) jud(e,i);
	}
	int d=gcd(up,down);
	if (d!=down) printf("%d/%d",up/d,down/d);
	else cout<<up/d;
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值