【链接】
https://www.luogu.com.cn/problem/P3705
【题解】
要求
C
=
a
1
′
+
a
2
′
+
.
.
.
+
a
n
′
b
1
′
+
b
2
′
+
.
.
.
+
b
n
′
C = \frac{a'_1+a'_2+...+a'_n}{b'_1+b'_2+...+b'_n}
C=b1′+b2′+...+bn′a1′+a2′+...+an′的最大值,首先要想到二分,类似的还有最大(小)值最小/大、平均数最大(小)问题。
对原式进行移项操作
a
1
′
+
a
2
′
+
.
.
.
+
a
n
′
b
1
′
+
b
2
′
+
.
.
.
+
b
n
′
>
=
x
\frac{a'_1+a'_2+...+a'_n}{b'_1+b'_2+...+b'_n} >= x
b1′+b2′+...+bn′a1′+a2′+...+an′>=x 转为
∑
a
i
−
x
∗
∑
b
i
>
=
0
\sum a_i - x * \sum b_i >= 0
∑ai−x∗∑bi>=0
二分
x
x
x 的值
重新建图,w[i][j] = A[i][j] - x*B[i][j]
使用KM算法检测当前最大匹配是否成功
可以用KM算法的原因,题目隐藏每个男女生都能找到舞伴的条件
【代码】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int INF = 1<<30;
const double eps = 1e-7;
int N,A[maxn][maxn],B[maxn][maxn];
double la[maxn],lb[maxn],W[maxn][maxn];
bool va[maxn],vb[maxn];
double delta = 0;
int match[maxn];
void read()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&A[i][j]);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&B[i][j]);
}
bool dfs(int x)
{
va[x] = 1;
for(int y=1;y<=N;y++)
{
if(vb[y]) continue;
if(la[x]+lb[y] - W[x][y] < eps)
{
vb[y] = 1;
if(!match[y] || dfs(match[y]))
{
match[y] = x;
return true;
}
}
else
{
delta = min(delta,la[x]+lb[y] - W[x][y]);//最小下降
}
}
return false;
}
bool KM(double mid)
{ for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
W[i][j] = 1.0*A[i][j] - mid*B[i][j];
memset(match,0,sizeof(match));
for(int i=1;i<=N;i++)
{
la[i] = -INF;
lb[i] = 0;
for(int j=1;j<=N;j++)
la[i] = max(la[i],W[i][j]);
}
for(int i=1;i<=N;i++)
{
while(1)
{
memset(va,0,sizeof(va));
memset(vb,0,sizeof(vb));
delta = INF;
if(dfs(i)) break;
for(int j=1;j<=N;j++)
{ if(va[j]) la[j]-=delta;
if(vb[j]) lb[j]+=delta;
}
}
}
double ans = 0;
for(int i=1;i<=N;i++)
ans+=W[match[i]][i];
return (ans>eps?1:0);
}
void work()
{
double l = 0,r = 1e4,mid;
while(r-l>eps)
{
mid = l+(r-l)/2;
if(KM(mid))
l = mid;
else
r = mid;
}
printf("%.6lf",l);
}
int main()
{
read();
work();
return 0;
}