题意:
有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; }