#include<bits/stdc++.h>
using namespace std;
struct edge
{
int w,u,v;
edge(int weight,int from,int to):w(weight),u(from),v(to){}
};
struct ans
{
int w1,w2;
ans(int ws,int wb):w1(ws),w2(wb){}
};
bool mycmp(edge a,edge b)
{
return(a.w<b.w);
}
vector<edge> e;
int p[550];
void init(int setsize)
{
for(int i=1;i<=setsize;i++){
p[i]=i;
}
}
int get(int x)
{
return(x==p[x]?x:p[x]=get(p[x]));
}
int unite(int x,int y)
{
int a=get(x); int b=get(y);
p[a]=b;
}
int gcd(int m,int n)
{
int r;
do{
r=m%n;
m=n;
n=r;
}while(r!=0);
return m;
}
int main()
{
int n,m,s,t;
ans a=ans(1,INT_MAX-1);
cin>>n>>m;
e.clear();
init(n);
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
e.push_back(edge(w,u,v));
}
cin>>s>>t;
/*判断是否存在路径*/
bool ok=false;
for(int i=0;i<m;i++){
const edge& st=e[i];
unite(st.u,st.v);
if(get(s)==get(t)){ok=true;init(n);break;}
}
if(not ok){cout<<"IMPOSSIBLE"<<endl; return 0;}
sort(e.begin(),e.end(),mycmp);
//cout<<e[2].w<<endl;
/*开始枚举*/
for(int i=0;i<m;i++){
init(n);
const int& w1=e[i].w;
for(int j=i;j<m;j++){
unite(e[j].u,e[j].v);
if(get(s)==get(t)){
const int& w2=e[j].w;
if((double)w2/w1<(double)a.w2/a.w1){a=ans(w1,w2);break;}
}
}
}
/*约分后输出即可*/
int gcdnum=gcd(a.w2,a.w1);
if(a.w1==gcdnum)cout<<a.w2/gcdnum<<endl;
else cout<<a.w2/gcdnum<<"/"<<a.w1/gcdnum<<endl;
return 0;
}
题意:给定n个点、m条边的带权无向图,求从点s到点t的一条路径,使得路径上所有边权的最大值与最小值之比最小,输出该比值即可(若不存在,输出”IMPOSSIBLE”)。n<=500,m<=5000,s<>t。
我考虑过用DP、Bellman-ford之类的方法同时维护路径的最大值与最小值,但感觉做不下去。于是想到类似Kruskal算法的解法:按权值从小到大对边进行排序。从小到大枚举所有边:设当前选择的边权值为w1,从该边开始,从小到大将边的左右端点合并到一个联通分量中,直到点s、t连通为止。记下最后选择的边的权值w2。比较w2/w1与当前的最小比值,若更优,则将最小比值更新为w2/w1。枚举完成后即可获得答案。
这里存在一个问题:假设我们当前选择了边i,合并到边j时s、t连通,边i有可能并不存在于s到t的路径上!这意味着我们计算的wj/wi也许根本就无意义。但,这并不会影响算法的正确性。我们分两种情况证明该算法的正确性:如图(图中的w1、w2是指边的编号而非上文说的w2 w1),r1,r2是st路径上必经的两边,而w1、w2……则是一些不会出现在st路径上的边,不妨假定r2的边权比任何一条wi都大。若r1<任意wi,则枚举wi边之前会先枚举r1,最小值w[r1]在路径上,又最大值w[r2]一定在路径上(因为加入该边使得st连通),我们就保证了w[r2]/w[r1]有意义。之后在枚举wi边时,因为不允许选取r1,所以无论如何也无法让st连通,故不会产生无意义解;若r1并不小于所有wi,假定w1是wi中边权最小的边,按照算法,我们会先选择w1,并得到一个解w[r2]/w[w1],这是无意义的,w1并不在路径上。然而,在之后的某个时刻我们将枚举到r1,并得到另一个解w[r2]/w[r1],因为w[r1]>w[w1],所以w[r2]/w[r1]