CODEVS 1001 舒适的路线 题解

来源:互联网 发布:施耐德base 知乎 编辑:程序博客网 时间:2024/05/17 04:05
#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]

2 0
原创粉丝点击