D142 最小生成树 Kruskal 算法+01分数规划 P4951 [USACO01OPEN] Earthquake
D142 最小生成树 Kruskal 算法+01分数规划 P4951 [USACO01OPEN] Earthquake_哔哩哔哩_bilibili
P4951 [USACO01OPEN] Earthquake - 洛谷
选择一些边使得 n 个节点连通,并且要最大化单位时间的利润。
思路
最大化和式的分数,显然是 01 分数规划
设所选的边集为 $S$,考虑二分一个答案 $x$,使得 $\frac{F-\sum{C_i}}{\sum{T_i}}\ge x,\;i\in S$ 成立,即 $F-\sum(x*T_i+C_i)\ge 0$ 成立
将边重新赋权为 $W_i=x*T_i+C_i$,对新边权跑最小生成树求和,如果 $F-sum\ge 0$,则说明答案可以更大

相关板子:
D08【模板】最小生成树 Kruskal 算法 - 董晓 - 博客园
// 最小生成树 kruskal+01分数规划 O(m*logm*log(1e9)) #include<bits/stdc++.h> using namespace std; const int N=405,M=10005; const double eps=1e-6; int n,m,F,fa[N]; struct A{int u,v,c,t; double w;}a[M]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } bool kruskal(double mid){ for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) a[i].w=mid*a[i].t+a[i].c; sort(a+1,a+m+1,[&](A a,A b){return a.w<b.w;}); int tot=0; double sum=0; for(int i=1;i<=m;i++){ int x=find(a[i].u),y=find(a[i].v); if(x!=y){ fa[x]=y; sum+=a[i].w; if(++tot==n-1) break; } } return F-sum>=0; } 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,r=1e9,mid; while(l+eps<r){ mid=(l+r)/2; if(kruskal(mid)) l=mid; else r=mid; } printf("%.4lf\n",l); }
浙公网安备 33010602011771号