题意:
有n个男生和n个女生,其中第i个男生和第j个女生配对的数据分别为aij和bij,要求找到一种配对方案,使∑aij/∑bij最大
题解:
拿到题目一看这个样子就考虑分数规划,首先二分答案,看是否存在满足∑aij/∑bij>=mid的解,若存在就意味着还可能有更大的解。建边时将两个人的配对的权值改成aij-bij*mid,跑费用流,答案大于等于0说明还可能有更优解。
最后注意一下精度问题。最后吐槽一下,自带大常数的我成功地被卡常了QAQ,最后还是简单卡了卡常数才过掉的
代码:
#include <bits/stdc++.h>
using namespace std;
double eps=1e-8;
int n,st,ed,hed[204],cnt;
double ans,v[204],res,a[101][101],b[101][101];
int inq[50001],w[204],h,t,f[50000],q[50001];
struct node
{
int next,from,to,c;
double cost;
}e[50001];
void add(int from,int to,int c,double cost)
{
++cnt;
e[cnt].from=from;
e[cnt].to=to;
e[cnt].c=c;
e[cnt].cost=cost;
e[cnt].next=hed[from];
hed[from]=cnt;
++cnt;
e[cnt].from=to;
e[cnt].to=from;
e[cnt].c=0;
e[cnt].cost=-cost;
e[cnt].next=hed[to];
hed[to]=cnt;
}
void bfs()
{
memset(w,0,sizeof(w));
memset(v,-0x3f,sizeof(v));
q[1]=st;
h=1,t=2;
w[st]=2e9;
v[st]=0;
while(h!=t)
{
int x=q[h];
inq[x]=0;
for(int i=hed[x];i;i=e[i].next)
{
int y=e[i].to;
if(e[i].c&&v[y]-v[x]-e[i].cost<-eps)
{
w[y]=min(w[x],e[i].c);
v[y]=v[x]+e[i].cost;
f[y]=i;
if(!inq[y])
{
q[t++]=y;
inq[y]=1;
}
}
}
++h;
}
for(int i=f[ed];i;i=f[e[i].from])
{
e[i].c-=w[ed];
e[i^1].c+=w[ed];
}
}
int check(double k)
{
cnt=1;
memset(hed,0,sizeof(hed));
for(int i=1;i<=n;++i)
{
add(st,i,1,0);
add(i+n,ed,1,0);
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
add(i,n+j,1,a[i][j]-k*b[i][j]);
}
}
res=0.0;
while(1)
{
bfs();
if(w[ed]>eps)
res+=(double)w[ed]*v[ed];
else
break;
if(res<-eps)
break;
}
if(res>=-eps)
return 1;
else
return 0;
}
double find()
{
double l=1,r=1e4,mid,ji=0;
while(r-l>eps)
{
mid=(l+r)/2.0;
if(check(mid))
ji=l=mid;
else
r=mid;
}
return ji;
}
int main()
{
scanf("%d",&n);
st=n+n+1,ed=n+n+2;
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]);
ans=find();
printf("%.6lf\n",ans);
return 0;
}