题目概述
给出n个点,m条双向边,每条边有边权,求一条s->t的路径,使得最大边权与最小边权的比值最小。
解题报告
我们有一个简单的初始想法,就是枚举MIN,然后二分枚举MAX,再用BFS判断s->t是否存在边权均在MIN~MAX内的路径。但是由于题目中的边权范围比较大,而每次BFS的效率是O(m),所以效率不是很高。其实有了这个想法,我们可以想到另外一种类似的想法:将边按照边权排序,然后枚举i和j,表示选择i~j的边,再检查s->t是否连通(其实就是变相枚举了MIN和MAX)。由于m不是特别大,所以这种想法是完全可行的!至于检查连通,用并查集即可。
示例程序
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=500,maxm=5000;
int n,m,st,gl,MIN,MAX,father[maxn+5];
double ans=1e100;
struct Edge
{
int x,y,z;
bool operator < (const Edge &c) const {return z<c.z;}
};
Edge e[maxm+5];
int getfa(int x)
{
if (father[x]==x) return x;
return father[x]=getfa(father[x]);
}
int gcd(int a,int b) {if (!b) return a; else return gcd(b,a%b);}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
scanf("%d%d",&st,&gl);
sort(e+1,e+1+m);
for (int i=1,j;i<=m;i++)
{
for (j=1;j<=n;j++) father[j]=j;
for (j=i;j<=m;j++)
{
int fx=getfa(e[j].x),fy=getfa(e[j].y);
if (fx!=fy) father[fx]=fy; else continue;
if (getfa(st)==getfa(gl)) break;
}
if (getfa(st)!=getfa(gl)) break;
//这里是个优化,getfa(st)!=getfa(gl)说明选i~m不满足,所以i+1~m肯定也不满足,不需要处理了
if ((double)e[j].z/e[i].z<ans) MIN=e[i].z,MAX=e[j].z,ans=(double)MAX/MIN;
}
if (ans==1e100) {printf("IMPOSSIBLE\n");return 0;}
int t=gcd(MIN,MAX);MAX/=t;MIN/=t;
if (MIN==1) printf("%d\n",MAX); else printf("%d/%d\n",MAX,MIN);
return 0;
}