【洛谷p4951】地震 #0/1分数规划入门#

0/1分数规划入门题:【洛谷p4951】地震 

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<deque>
using namespace std;
typedef long long ll;

/*【p4951】地震
将图连通,使总利润(F-成本)和总施工时间的比值最大。 */

/*【0/1分数规划】推式子 + 二分答案 + 最小生成树
( f − ∑wi ) / ∑ti ≥ x [即now_mid];化简为:f≥∑(wi+ti*x)。
每二分一个答案x,求最小生成树的∑(wi+ti*x)是否满足<=f。*/

void reads(int &x){ //读入优化(正负整数)
    ll f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f; //正负号
}

const int N=1019,M=20019;
const double eps=1e-6;

int n,m,F,fa[N];

struct Edge{ int u,v,c,t; double w; }a[M];

bool cmp(Edge x,Edge y){ return x.w<y.w; } //最小生成树边集排序

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

bool checks(double mid){
    for(int i=1;i<=n;i++) fa[i]=i; //记得并查集清零
    for(int i=1;i<=m;i++) a[i].w=a[i].c+mid*a[i].t;
    sort(a+1,a+m+1,cmp); //边按权值排序
    int cnt_=0; double sum=0;
    for(int i=1;i<=m;i++){ if(cnt_==n-1) break;
        int fx=find_fa(a[i].u),fy=find_fa(a[i].v);
        if(fx!=fy) cnt_++,fa[fx]=fy,sum+=a[i].w;
    } return (sum<=F);
}

int main(){
    scanf("%d%d%d",&n,&m,&F);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].c,&a[i].t);
    double l=0.0,r=1e9,mid; //↓↓注意实数二分的写法
    while(r-l>1e-6){ mid=(l+r)/2; if(checks(mid)) l=mid; else r=mid; }
    printf("%.4lf
",l); return 0;
}
原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/10103360.html