[Sdoi2017]新生舞会(分数规划+费用流)
题解:二分答案mid,然后将每个位置看成a-b*mid,然后由于是n个男生和n个女生匹配,每个人搭配一个cp,于是有点类似于https://www.lydsy.com/JudgeOnline/problem.php?id=1070(费用流模板题),加边(S,i,1,0),(i+n,T,1,0),(i,j+n,1,a[i][j]-mid*b[i][j])(注:括号内分别为始边、终边、流量、费用),跑最大费用最大流,若大于0则调整下界,否则调整上界,直至上下界基本重合。
#include<bits/stdc++.h> using namespace std; const int N=207; struct edge{int u,v,w,nxt;double c;}e[N*N*4]; queue<int>q; int n,S,T,ecnt,hd[N],vis[N],pre[N]; double a[N][N],b[N][N],d[N]; void adde(int x,int y,int z,double c) { e[++ecnt]=(edge){x,y,z,hd[x],c},hd[x]=ecnt; e[++ecnt]=(edge){y,x,0,hd[y],-c},hd[y]=ecnt; } bool spfa() { for(int i=S;i<=T;i++)d[i]=-1e18,pre[i]=0; d[S]=0,q.push(S); while(!q.empty()) { int u=q.front();q.pop(); vis[u]=0; for(int i=hd[u];i;i=e[i].nxt) if(e[i].w&&d[u]+e[i].c>d[e[i].v]) { pre[e[i].v]=i,d[e[i].v]=d[u]+e[i].c; if(!vis[e[i].v])q.push(e[i].v),vis[e[i].v]=1; } } return d[T]>-1e18; } bool check() { double ret=0; while(spfa()) { int mn=1e9+7; for(int i=pre[T];i;i=pre[e[i^1].v])mn=min(mn,e[i].w); for(int i=pre[T];i;i=pre[e[i^1].v])e[i].w-=mn,e[i^1].w+=mn; ret+=mn*d[T]; } return ret>0; } int main() { scanf("%d",&n),T=2*n+1; for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%lf",&a[i][j]); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%lf",&b[i][j]); double l=0,r=10086,mid; while(r-l>1e-7) { mid=(l+r)/2; ecnt=1; memset(hd,0,sizeof hd); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) adde(i,j+n,1,a[i][j]-mid*b[i][j]); for(int i=1;i<=n;i++)adde(S,i,1,0),adde(i+n,T,1,0); if(check())l=mid;else r=mid; } printf("%.6lf",l); }