描述
地震已经破坏了农夫约翰所有的农场以及所有连接农场的道路。作为一个意志坚强的人,他决定重建所有的农场。在重建全部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。
标签
USACO_2001_open_GREEN
一个简单的最优比率生成树。
要求的是
F−∑c[i]∑t[i]
F
−
∑
c
[
i
]
∑
t
[
i
]
的最大值。
变了形就是
F−∑(c[i]+k∗t[i])>=0
F
−
∑
(
c
[
i
]
+
k
∗
t
[
i
]
)
>=
0
这个就是一个简单的01分数规划+最小生成树。
代码:
#include<bits/stdc++.h>
#define N 405
#define M 10005
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,m,first[N],F,fa[N];
struct Node{int u,v,c,t;double w;}e[M];
inline bool cmp(Node a,Node b){return a.w>b.w;}
inline int find(int x){return x==fa[x]?fa[x]:fa[x]=find(fa[x]);}
inline bool kruskal(double mid){
double ret=0.0;
int cnt=0;
for(int i=1;i<=m;++i)e[i].w=-1.0*e[i].t*mid-e[i].c*1.0;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1;i<=m;++i){
if(cnt==n-1)break;
int fx=find(e[i].u),fy=find(e[i].v);
if(fx!=fy)fa[fx]=fy,ret+=e[i].w;
}
return ret+F>=0;
}
int main(){
n=read(),m=read(),F=read();
for(int i=1;i<=m;++i)e[i].u=read(),e[i].v=read(),e[i].c=read(),e[i].t=read();
double l=0.0,r=60000;
while(r-l>1e-7){
double mid=(l+r)/2;
if(kruskal(mid))l=mid;
else r=mid;
}
printf("%.4lf",l);
return 0;
}