ECNU 3247 最小生成树+二分

来源:互联网 发布:白金数据迅雷下载 编辑:程序博客网 时间:2024/05/29 11:18

题意

中文题,不解释

题解

原本以为是一道普通的最小生成树问题,但是搞了很久都没办法控制最优选择顺序。后来看了题解才恍然大悟,怎么说呢,感觉很神奇。通过二分搜索去凑解,看似复杂度很高,但是计算一下就会发现复杂度只有O(n*log^2(n)),处于可以接受的范围。

注意事项

一定要注意kruskal算法里的变量要用double,在这里DEBUG了半天。

代码

#include <bits/stdc++.h>#define INF 10000010#define MAXN 100010#define EPS 1e-7using namespace std;typedef long long ll;int n,m;struct Edge{    int from,to,type;    double weight;};bool cmp(Edge a,Edge b){    return a.weight<b.weight;}Edge edges[MAXN] ;int p[MAXN];void init(){    for(int i=1;i<=n;i++){        p[i]=i;    }}int find(int x){    return x==p[x]?x:p[x]=find(p[x]);}void unite(int x,int y){    p[find(x)]=find(y);}bool same(int x,int y){    return find(x)==find(y);}double kurskal(){    sort(edges,edges+m,cmp);    init();    double cost=0;    for(int i=0;i<m;i++){        Edge e=edges[i];        if(!same(e.from,e.to)){            unite(e.from,e.to);            cost+=e.weight;        }    }    return cost;}int main(){    ll M;    scanf("%d%d%lld",&n,&m,&M);    for(int i=0;i<m;i++){        int u,v,t,f;        scanf("%d%d%d%d",&u,&v,&t,&f);        edges[i].from=u;        edges[i].to=v;        edges[i].weight=t;        edges[i].type=f;    }    double low=1,up=(double)M;    double mid=1;    while(up-low>EPS){        mid=(low+up)/2;        for(int i=0;i<m;i++){            if(edges[i].type)                edges[i].weight*=mid;        }        double ans=kurskal();        for(int i=0;i<m;i++){            if(edges[i].type)                edges[i].weight/=mid;        }        if(ans-M>EPS){            up=mid;        }else{            low=mid;        }    }    printf("%lf\n",mid);}
原创粉丝点击