一、题目
二、解法
原题要我们求的是 C = ∑ i = 1 n a i ∑ i = 1 n b i C=\frac{\sum_{i=1}^{n} a_i}{\sum_{i=1}^{n}b_i} C=∑i=1nbi∑i=1nai,我们尝试对原式进行等价变化:
∑ i = 1 n a i = C × ∑ i = 1 n b i ⇒ ∑ i = 1 n a i − C × b i = 0 \sum_{i=1}^n a_i=C\times \sum_{i=1}^n b_i\Rightarrow \sum_{i=1}^na_i-C\times b_i=0 ∑i=1nai=C×∑i=1nbi⇒∑i=1nai−C×bi=0
考虑二分 C C C,问题就变成了使上式最大,如果上式大于等于 0 0 0,那么就满足条件,往更大 C C C二分,否则往更小 C C C二分,原问题本质是一个二分图匹配,我们可以跑最大费用流,图这样建:
- 汇点连所有的男生,边权为 0 0 0,容量为 1 1 1。
- 所有的男生连所有的女生,边权为 a i − C × b i a_i-C\times b_i ai−C×bi,容量为 i n f inf inf。
- 所有的女生连汇点,边权为 0 0 0,容量为 1 1 1。
#include <cstdio>
#include <queue>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-7
#define db double
const int MAXN = 205;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,tot,S,T,f[MAXN],a[105][105],b[105][105];
int flow[MAXN],lst[MAXN],pre[MAXN];db dis[MAXN];
db ans;
struct edge
{
int v;db c;int f,next;
}e[MAXN*MAXN];
struct node
{
int u;db c;
bool operator < (const node &R) const
{
return c<R.c;
}
};
priority_queue<node> q;
void add_edge(int u,int v,db c,int fl)
{
e[++tot]=edge{v,c,fl,f[u]},f[u]=tot;
e[++tot]=edge{u,-c,0,f[v]},f[v]=tot;
}
bool bfs()
{
for(int i=S;i<=T;i++)
dis[i]=-inf;
flow[S]=inf;dis[S]=0;pre[S]=-1;
q.push(node{S,0});
while(!q.empty())
{
int u=q.top().u;db t=q.top().c;
q.pop();
if(dis[u]>t) continue;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;db c=e[i].c;
if(dis[v]<dis[u]+c && e[i].f>0)
{
dis[v]=dis[u]+c;
pre[v]=u;
lst[v]=i;
flow[v]=min(flow[u],e[i].f);
q.push(node{v,dis[v]});
}
}
}
return dis[T]!=-inf;
}
db get()
{
db cost=0;
while(bfs())
{
cost+=flow[T]*dis[T];
int cur=T;
while(cur^S)
{
e[lst[cur]].f-=flow[T];
e[lst[cur]^1].f+=flow[T];
cur=pre[cur];
}
}
return cost;
}
bool check(double c)
{
S=0;T=2*n+1;
tot=1;
for(int i=S;i<=T;i++)
f[i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
add_edge(i,j+n,1.0*a[i][j]-c*b[i][j],inf);
for(int i=1;i<=n;i++)
add_edge(S,i,0,1);
for(int i=1;i<=n;i++)
add_edge(i+n,T,0,1);
return get()>=0;
}
void solve(db l,db r)
{
if(r-l<eps) return ;
db mid=(l+r)/2;
if(check(mid))
{
ans=mid;
solve(mid,r);
}
else
solve(l,mid);
}
int main()
{
n=read();
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();
solve(0,10000);
printf("%.6lf\n",ans);
}