洛谷 P2502 旅行 SSL 1312 (并查集暴力)

题目描述:

题目传送门


解题思路:

遇到这种,在满足某种条件下找最值的问题:如这题,在一条能够从起始点到达目标点的路径中找到比值最小的最大值和最小值。我们往往会先枚举这些可行的路径,然后在路径里面找到最大值和最小值,这样子的话很难找到头绪。
我们可一个换个思路,枚举路径的权值的最小值的下限 i i i 和最大值的上限 j j j
然后把符合这个上下限权值条件的边上的点所在的集合都按边的关系合并,也就是说若 a a a b b b 之间有一条满足条件的边,则将 a a a b b b 所在的集合合并。

如图:
在这里插入图片描述
我们可以在最小边权 i i i 到最大边权 j j j 的区间内选边进行构造路径。

最后判断选用的这些边构成的路径是否能从起始点道目标点,也就是说判断合并后起始点是否和目标点在一个集合。
若能,则尝试用 i i i j j j 更新最小值和最大值;若不能,则枚举下一个最小值最大值区间。

因此我们不断枚举并改变边权的上下限,在满足从起始点到目标点能构成路径的那些上下限内进行最优选择,找到比值最小的一组上下限即为答案。

由于要枚举上下限区间,因此很显然我们要将边按照边权排序,然后枚举边权的下限以及上限,一次来推出我们的边权区间范围,最后通过合并集合来判断是否构成路径。


CODE:

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,s,f,x,y;
double minn=0.0;
int fa[1010];
struct Gar
{
	int x,y,v;
} e[5010];
bool cmp(Gar a,Gar b)  //按边权从小到大排序
{
	return a.v<b.v;
}
int find(int x)  //查找集合代表元素
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void join(int x,int y)  //合并集合
{
	fa[find(x)]=fa[find(y)];
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	  cin>>e[i].x>>e[i].y>>e[i].v;
	cin>>s>>f;
	sort(e+1,e+m+1,cmp);
	minn=1e9*1.0;
	for(int i=1;i<=m;i++)  //枚举最小边权的边
	  {
	  	for(int j=1;j<=n;j++) fa[j]=j;
	  	int j=i;
	  	bool flag=false;
	  	while(true)  //枚举最大的边
	  	  {
	  	  	if(j>m) break;
	  	  	join(e[j].x,e[j].y);  //将选用的边的两个点的集合合并
	  	  	if(find(s)==find(f)) //表示找到一条路径从起始点到达目标点
	  	  	  {
	  	  	  	flag=true;
	  	  	  	break;
			  }
			j++;  //由于边权排序,因此越往后越大,直接枚举下一个最大边即可
		  }
		if(flag)
		  {
		  	if(double(e[j].v)/double(e[i].v)<minn)  //存下最小的比值
		  	  {
		  	  	minn=double(e[j].v)/double(e[i].v)*1.0;
		  	  	x=e[i].v;
		  	  	y=e[j].v;
			  }
		  }
	  }
	if(minn!=1e9)
	  {
	  	if(y%x==0) cout<<y/x;
	  	else
	  	  cout<<y/__gcd(x,y)<<"/"<<x/__gcd(x,y);  //输出最简分数形式
	  	return 0;
	  }
	cout<<"IMPOSSIBLE";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值