BZOJ 4819 [Sdoi2017]新生舞会 ——费用流 01分数规划
比值最大 分数规划
二分答案之后用费用流进行验证。
据说标称强行乘以1e7换成了整数的二分。
不过貌似实数二分也可以过。
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define ll long long #define mp make_pair #define eps 1e-7 #define inf 1e9 #define maxn 50005 int h[maxn],to[maxn],ne[maxn],en=0,fl[maxn],n,S=maxn-2,T=maxn-1; double cost[maxn],a[101][101],b[101][101],dis[maxn]; int with[maxn],minn[maxn],inq[maxn]; void add(int a,int b,double c,int d) { to[en]=b;ne[en]=h[a];fl[en]=d;cost[en]=c; h[a]=en++; to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++; } queue <int> q; bool SPFA() { F(i,1,2*n) dis[i]=inf; dis[S]=inf; dis[T]=inf; memset(inq,0,sizeof inq); memset(with,0,sizeof with); memset(minn,0x3f,sizeof minn); q.push(S); inq[S]=1; dis[S]=0; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0; for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0) { dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(minn[x],fl[i]); with[to[i]]=i; if (!inq[to[i]]) q.push(to[i]),inq[to[i]]=1; } } return dis[T]<inf-eps; } double zeng() { for (int i=T;i!=S;i=to[with[i]^1]) { fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; } return minn[T]*dis[T]; } double dinic() { double ret=0,tmp; while (SPFA()) { tmp=zeng(); ret+=tmp; } return ret; } bool check(double d) { memset(h,-1,sizeof h); en=0; F(i,1,n) add(S,i,0,1); F(i,1,n) add(i+n,T,0,1); F(i,1,n) F(j,1,n) add(i,j+n,d*b[i][j]-a[i][j],1); double ret=dinic(); if (ret<0) return true; return false; } int main() { scanf("%d",&n); F(i,1,n) F(j,1,n) scanf("%lf",&a[i][j]); F(i,1,n) F(j,1,n) scanf("%lf",&b[i][j]); double l=0,r=1e4; while (fabs(l-r)>eps) { double mid=(l+r)/2; if (check(mid)) l=mid; else r=mid; } printf("%.6lf\n",(l+r)/2); }