题目描述
题解
判断是否连通很简单,直接上ufs就可以了。
但是接下来的做法我有过一些错误的想法。一看上去觉得这题很像刚做过的最优贸易那道题,所以一开始用spfa维护了一坨最大值和最小值。但是这样实际上是不可行的。因为如果要使最大值与最小值的比最小的话,就是要让最大值尽可能小,最小值尽可能大。如果维护了这两个量的话,一组合原先的最小值就有可能变成大的。这样就很不科学了。
正确的做法应该这样思考:从目标考虑,最大值与最小值的比最小,如果我们现在确定了最小值的话,那么整条路线上的最大值应该是越小越好。那么我们可以枚举最小值,然后在图中求能使s和t连通的最小的最大值。假设说最小值为min,最大值为max,将边权在min~max之间的边全部加到图里,如果s和t连通了,则这是一个合法的方案。
那么这就是一个很科学的做法了!先将所有的边排序,然后枚举图中的最小值,从最小值的那条边起,依次向图中加边,知道s和t联通。那么此时的最小值和最大值就是一组合法的方案,更新就可以了。
判断连通什么的用ufs维护一下。时间复杂度
O(m2α)
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define N 505
#define M 5005
#define INF 30000
int n,m,x,y,z,s,t,up,down;
double ans;
int f[N];
struct hp{int x,y,z;}edge[M];
int cmp(hp a,hp b)
{
return a.z<b.z;
}
int find(int x)
{
if (x==f[x]) return x;
f[x]=find(f[x]);
return f[x];
}
int gcd(int a,int b)
{
if (!b) return a;
else return gcd(b,a%b);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) f[i]=i;
for (int i=1;i<=m;++i)
{
scanf("%d%d%d",&x,&y,&z);
int f1=find(x),f2=find(y);
if (f1!=f2) f[f1]=f2;
edge[i].x=x,edge[i].y=y,edge[i].z=z;
}
scanf("%d%d",&s,&t);
if (find(s)!=find(t))
{
puts("IMPOSSIBLE");
return 0;
}
ans=INF;
sort(edge+1,edge+m+1,cmp);
for (int l=1;l<=m;++l)
{
bool flag=false; int r;
for (int i=1;i<=n;++i) f[i]=i;
for (int i=l;i<=m;++i)
{
int f1=find(edge[i].x),f2=find(edge[i].y);
if (f1!=f2) f[f1]=f2;
if (find(s)==find(t))
{
r=i;
flag=true;
break;
}
}
if (flag)
{
double now=(edge[r].z+0.0)/(edge[l].z+0.0);
if (now<ans) ans=now,up=edge[r].z,down=edge[l].z;
}
}
int t=gcd(up,down);
if (t!=down) printf("%d/%d\n",up/t,down/t);
else printf("%d\n",up/t);
}
总结
①以后要好好读题。想出做法了之后算算样例再写。
②要求两个量的情况要注意一种思路:枚举一个量然后求另一个量。非常显然但是也十分容易忽略。