bzoj 1050

一条从S到T的路径的答案为MAX/MIN。

无法直接获得最优解。

所有边按边权从大到小排序,从大到小枚举MIN,要MAX尽量小。

问题变成给你i条无向边,使S与T联通且使边权的最大值最小。

这不就是最小生成树的性质吗?Kruskal算法。

从后往前枚举每一条边,如果边连接的两个节点不在同一集合,就合并,直到S与T在同一集合。

最小的MAX即是枚举到的最后一条边。

交叉相乘更新答案。用辗转相除法把答案变成既约分数。

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
int read(){
    char c; while(!isdigit(c=getchar())); int x=c-'0';
    while(isdigit(c=getchar())) x=x*10+c-'0'; return x;
}
struct edge{
    int u,v,d;
}e[5001];
bool cmp(edge x,edge y){
    return x.d>y.d;
}
int fa[501];
int find(int x){
    return x==fa[x]? x:fa[x]=find(fa[x]);
}
int gcd(int x,int y){
    return y==0? x:gcd(y,x%y);
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=m;i+=1)
        e[i].u=read(),e[i].v=read(),e[i].d=read();
    sort(e+1,e+m+1,cmp);
    int s=read(),t=read(),res1=0,res2=0;
    for(int i=1;i<=m;i+=1){
        int ans=0;
        for(int j=1;j<=n;j+=1) fa[j]=j;
        for(int j=i;j>=1;j-=1){
            int x=find(e[j].u),y=find(e[j].v);
            if(x!=y) fa[x]=y;
            if(find(s)==find(t)){
                ans=e[j].d; break;
            }
        }
        if(ans)
            if((!res1 && !res2) || res1*e[i].d>res2*ans){
                int x=gcd(e[i].d,ans);
                res1=ans/x; res2=e[i].d/x;
            }
    }
    if(!res1 && !res2) puts("IMPOSSIBLE");
    else if(res1%res2) printf("%d/%d",res1,res2); 
    else printf("%d",res1/res2);
    return 0;
}
原文地址:https://www.cnblogs.com/AmnesiacVisitor/p/7657577.html