费用流 分数规划
很显然这是个完美匹配问题。可以KM做,也可以费用流。KM要比费用流快很多,这里采用费用流。
分数规划套路一波。二分答案 mid m i d ,男生到女生连费用为 a[i][j]−mid∗b[i][j] a [ i ] [ j ] − m i d ∗ b [ i ] [ j ] ,容量为1的边,源点到每个男生和每个女生到汇点连费用为0,容量为1的边。跑最大费用最大流,如果费用 ≥0 ≥ 0 则可行。
费用流有点卡常。可以写我不会的 zkw或者不要用结构体。
代码:
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 205
#define M N*N
#define F inline
#define eps 1e-8
using namespace std;
typedef double DB;
int nxt[M],to[M],v[M],ef[M],fx[N],e[N];
int n,k,s,t,a[N][N],b[N][N],h[N],q[N*N],rem[N];
DB d[N],ed[M]; bool f[N];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF; return *l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F void addedge(int x,int y,DB z){
nxt[++k]=h[x],to[k]=y,v[k]=1,ef[k]=0,ed[k]=z,h[x]=k;
nxt[++k]=h[y],to[k]=x,v[k]=ef[k]=0,ed[k]=-z,h[y]=k;
}
F bool spfa(){
for (int i=s;i<=t;i++)
f[i]=false,d[i]=-1e10;
int l=0,r=1; rem[s]=1e9,d[q[1]=s]=0;
while (l<=r){
int x=q[++l]; f[x]=false;
for (int i=h[x],p;~i;i=nxt[i])
if (v[i]>ef[i]&&d[x]+ed[i]>d[p=to[i]]){
rem[p]=min(rem[x],1);
d[p]=d[x]+ed[i],fx[p]=x,e[p]=i;
if (!f[p]) q[++r]=p,f[p]=true;
}
}
return d[t]>-1e10?rem[t]:0;
}
F void mdfy(int sum){
int x=t;
while (x!=s)
ef[e[x]]+=sum,ef[e[x]^1]-=sum,x=fx[x];
}
F bool pd(DB mid){
DB cst=0; k=-1;
for (int i=s;i<=t;i++) h[i]=-1;
for (int i=1;i<=n;i++)
addedge(s,i,0),addedge(i+n,t,0);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
addedge(i,j+n,1.0*a[i][j]-mid*b[i][j]);
for (int sum=spfa();sum;sum=spfa())
cst+=d[t]*sum,mdfy(sum);
return cst>-eps;
}
int main(){
n=_read(),t=n<<1|1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=_read();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
b[i][j]=_read();
DB l=0,r=1e7,mid;
while (r-l>eps)
if (pd(mid=(l+r)/2)) l=mid+eps;
else r=mid-eps;
return printf("%.6f\n",l),0;
}