[HAOI2006] 旅行

题面

观察到题目要求比值,可以想到分数规划,但题目求的是一条路径上最大值最小值的比,所以不能分数规划,又考虑到直接暴力dfs搜索,但n,m的范围为500,很明显dfs会炸飞

考虑到一条路径的权值只由最大值最小值决定,所以当我们假定最大最小值后,我们可以将求解一条最大值最小值比值最小的路径转化为判断是否存在一条路径使路径上的最大值最小值都在我们假定的范围内,这时候,我们可以将假定范围内的所有边都选上,然后判断起点与终点的连通性;那么,问题转化到了如何快速判断两点之间的连通性,很好联想到用并查集维护

所以,我们要做的就是将边按权值排序,假定一个最大边和最小边,将中间的边都加进并查集,判断起点终点连通性如果联通,说明这是一组合法解,我们找到比值最小的合法解就是答案

using namespace std;
#define ll long long
#define MAXN 5010
#define db double
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
struct node{
	ll f[MAXN],siz;
	void mk(){
		for(int i=0;i<=siz;i++)f[i]=i;
	}
	ll find(ll x){
		return f[x]==x?x:f[x]=find(f[x]);
	}
	void merge(ll x,ll y){
		f[find(x)]=find(y);
	}
};
struct node_line{
	ll beg,ed,val;
};
bool cmp(node_line a,node_line b){
	return a.val<b.val;
}
void write(ll x,ll y){
	ll g=__gcd(x,y);
	while(g!=1){
		x/=g,y/=g;
		g=__gcd(x,y);
	}
	if(y==1)cout<<x;
	else cout<<x<<"/"<<y;
}
node t;
node_line e[MAXN];
ll n,m;
db ans=1e9;
ll fz,fm;
int main(){
	n=read(),m=read();
	t.siz=n;
	t.mk();
	for(int i=1;i<=m;i++){
		e[i].beg=read(),e[i].ed=read(),e[i].val=read();
		t.merge(e[i].beg,e[i].ed);
	}
	ll st=read(),ed=read();
	if(t.find(st)!=t.find(ed)){
		cout<<"IMPOSSIBLE";
		return 0;
	}
	sort(e+1,e+1+m,cmp);
	for(int i=1;i<=m;i++){
		t.mk();
		for(int j=i;j<=m;j++){
			t.merge(e[j].beg,e[j].ed);
			if(t.find(st)==t.find(ed)){
				db now=(db)e[j].val/(db)e[i].val;
				if(now<ans){
					ans=now;
					fz=e[j].val,fm=e[i].val;
				}
			}
		}
	}
	write(fz,fm);
	return 0;
}
posted @ 2024-11-25 17:15  flyfreemrn  阅读(11)  评论(0)    收藏  举报