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; }