2018.09.12【USACO 2001 open GREEN】重建家园(最优比率生成树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82668442

描述

地震已经破坏了农夫约翰所有的农场以及所有连接农场的道路。作为一个意志坚强的人,他决定重建所有的农场。在重建全部N(1 <= N <= 400)个农场之前,首先必须把所有农场用道路连接起来,即任意两个农场之间必须有至少一条通路。

在研究了地图之后,农夫约翰已经得出了结论:M(1 <= M <= 10,000)条双向的道路可以在较短的时间内建造好。由于约翰的资金有限,他想以尽可能便宜的方法完成工程。

碰巧,农场里的奶牛们组建了一个专门从事重新改造在地震中被破坏的农场道路的工程公司,约翰决定把道路重建的工作交给奶牛们去完成。这些牛也有着锐利的商务感觉,它们希望从工程中获得最大的利益。

约翰和奶牛们经过协商,约翰愿意拿出F (1 <= F <= 2,000,000,000)元钱给奶牛们用于道路重建,要求奶牛们把所有农场用道路连接起来,即任意两个农场之间必须有至少一条通路。

奶牛们根据地图估算出了建造每条道路的成本c(1 <= c <= 2,000,000,000)及用时t(1 <= t <= 2,000,000,000),一对农场之间可以有1条以上可重建道路,并且对于给定的测试数据将所有农场连接起来是能够做到的。

现在奶牛们找到你,要求你编一个程序求出重建农场道路能让奶牛们获得的最大利润率。也就是使得剩余经费与所花时间的比值(赚钱速度)最大。

输入

输入文件包括m+1行,第一行为三个整数 N, M和 F,第二行到第M+1行,每一行都包括4个用空格隔开的整数:i, j, c,和 t分别表示可以重建的一条道路的两端连接的农场,以及重建该条道路的成本和时间。

输出

包含一个实数,表示剩余经费与所花时间的比值,保留4位小数。如果不可能以现有经费连接所有道路,输出0.0000。

样例输入

5 5 100
1 2 20 5
1 3 20 5
1 4 20 5
1 5 20 5
2 3 23 1

样例输出

1.0625

提示

样例解释 奶牛们可以选择修建最后的四条道路,这样所花经费为83,利润为17,所用时间为16,所以,他们在16单位时间内获利17元,所以比值为(100-83)/16=1.0625。


解析:

好的一看就是一道最优比率生成树。

但似乎有一点不对劲?

这个总经费是什么鬼?

但其实处理很简单,考虑我们只会选 n 1 条边,那么每条边的收益变成 F / ( n 1 ) c o s t ,然后就是一个裸的最优比率生成树板子


代码(Dinkelbach):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
    re int num=0;
    re char c;
    while(!isdigit(c=gc()));
    while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
    return num;
}

inline 
double getdb(){
    re double x=0,y=1.0;
    re char c=0;
    re bool f=1; 
    for(;!isdigit(c);c=gc())if(c=='-')f=0;
    for(;isdigit(c);c=gc())x=x*10+(c^48);
    if(c!='.')return f?x:-x;
    for(c=gc();isdigit(c);c=gc())x+=(y/=10)*(c^48);
    return f?x:-x;
}

struct Edge{
    int u,v;
    double t,c;
    double val;
    friend bool operator<(cs Edge &a,cs Edge &b){
        return a.val<b.val;
    } 
}e[10002];

int n,m;
double F;
int fa[401];

inline
int getfa(int x){
    return x==fa[x]?x:fa[x]=getfa(fa[x]);
}

inline
void merge(int i,int j){
    i=getfa(i);
    j=getfa(j);
    fa[i]=j;
}

inline
double kruskal(double L){
    for(int re i=1;i<=n;++i)fa[i]=i;
    int tot=0;
    for(int re i=1;i<=m;++i)e[i].val=e[i].c-e[i].t*L;
    sort(e+1,e+m+1);
    double x=0,y=0;
    for(int re i=m;i>=1;--i){
        int u=getfa(e[i].u);
        int v=getfa(e[i].v);
        if(u!=v)x+=e[i].c,y+=e[i].t,merge(u,v),++tot;
        if(tot>=n-1)break;
    }
    return x/y;
}

signed main(){
    n=getint();
    for(int re i=1;i<=n;++i)fa[i]=i;
    m=getint();
    F=getint();
    F=F/(n-1);
    for(int re i=1;i<=m;++i){
        e[i].u=getint();
        e[i].v=getint();
        e[i].c=F-getdb();
        e[i].t=getdb();
        merge(e[i].u,e[i].v);
    }
    int cnt=0;
    for(int re i=1;i<=n;++i)if(fa[i]==i)++cnt;
    if(cnt>1){
        puts("0.0000");
        return 0;
    }
    double L=0.0,ans=0.0;
    while(true){
        L=ans;
        ans=kruskal(L);
        if(fabs(ans-L)<=1e-8)break;
    }

    printf("%.4lf",ans>0?ans:0.0000);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82668442