码迷,mamicode.com
首页 > 其他好文 > 详细

[USACO 2001 OPEN]地震

时间:2019-02-12 20:12:23      阅读:190      评论:0      收藏:0      [点我收藏+]

标签:printf   哪些   int   0.00   获得   分析   lin   sum   return   

传送门

题意:N个牧场,可供修建的道路有M条.奶牛负责修建道路,将所有牧场连通,获得报酬F.每条道路都有自己的施工时间ti和建造成本ci.连接两个相同的牧场的道路可能有多条.保证所有的牧场必定是可连通的,不过也有可能一些道路的建造成本之和会超过F.求选择修建哪些道路,使单位时间的利润(即总利润和总施工时间的比值)的最大.

分析:理解清楚题意,可以得出我们要求的实际上就是\((F-\sum{c_i})/\sum{t_i}\)

\(x=(F-\sum{c_i})/\sum{t_i}\)

整理一下得,\(F-(\sum{c_i}+\sum{t_i}*x)=0\)

\(f(x)=F-(\sum{c_i}+\sum{t_i}*x)\)

f(x)随x增大而减小,即f(x)是一个递减函数,既然具有单调性,就要想到二分答案.

我们直接二分要求的答案x,每一次check时把每条边的边权wi更新为\(ci+ti*x\),然后跑一遍最小生成树,最后判断权值总和与F的大小关系.

注意本题是实数域上的二分答案

const double eps=1e-6;
//题目要求保留4位小数,精度可以设为1e-(4+2)
int n,m,f,fa[405];
struct edge{
    int x,y,c,t;
    double w;//注意边权是实数
}e[10005];
bool cmp(edge x,edge y){return x.w<y.w;}
int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
bool check(double mid){
    for(int i=1;i<=m;i++)
        e[i].w=e[i].c*1.0+e[i].t*mid;
//每一次check时,根据mid值更新边权
//kruskal跑最小生成树:
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)fa[i]=i;
    int k=0;double now=0;
    for(int i=1;i<=m;i++){
        int x=find(e[i].x),y=find(e[i].y);
        if(x!=y){
            k++;
            fa[y]=x;
            now+=e[i].w;
            if(now>f)return 0;
            if(k==n-1)break;
        }
    }
    return 1;
}
int main(){
    n=read();m=read();f=read();
    for(int i=1;i<=m;i++){
        e[i].x=read();
        e[i].y=read();
        e[i].c=read();
        e[i].t=read();
    }
    double l=-1e9,r=1e9,mid;
//注意一下二分边界,因为可能出现利润为负数的情况
    while(l+eps<r){//实数域上的二分答案模板
        mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    if(r<0)puts("0.0000");//如果利润为负数
    else printf("%.4lf\n",r);
    return 0;
}

[USACO 2001 OPEN]地震

标签:printf   哪些   int   0.00   获得   分析   lin   sum   return   

原文地址:https://www.cnblogs.com/PPXppx/p/10366851.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!