题目描述
学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。
有 nn 个男生和 nn 个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴。
Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出
ai,j
a
i
,
j
Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 bi,j b i , j ,表示第i个男生和第j个女生一起跳舞时的不协调程度。
当然,还需要考虑很多其他问题。
Cathy想先用一个程序通过 ai,j a i , j 和 bi,j b i , j 。求出一种方案,再手动对方案进行微调。
Cathy找到你,希望你帮她写那个程序。
一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是 a′1,a′2,...,a′n a 1 ′ , a 2 ′ , . . . , a n ′ ,假设每对舞伴的不协调程度分别是 b′1,b′2,...,b′ b 1 ′ , b 2 ′ , . . . , b ′ 。令
Cathy希望C值最大。
输入输出格式
输入格式:
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示 ai,j a i , j 。
接下来n行,每行n个整数,第i行第j个数表示 bi,j b i , j 。
输出格式:
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等。
输入输出样例
输入样例#1:
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
输出样例#1:
5.357143
说明
对于10%的数据, 1≤n≤5 1 ≤ n ≤ 5
对于40%的数据, 1≤n≤18 1 ≤ n ≤ 18
另有20%的数据, bi,j≤1 b i , j ≤ 1
对于100%的数据, 1≤n≤100,1≤ai,j,bi,j<=104 1 ≤ n ≤ 100 , 1 ≤ a i , j , b i , j <= 10 4
分析:显然可以分数规划一下,然后最大权匹配用费用流跑就可以了。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
const int maxv=3e4+7;
const int maxn=307;
const double inf=100000000000;
using namespace std;
int n,cnt,flow;
int s,t;
int ls[maxn],vis[maxn],pre[maxn];
double a[maxn][maxn],b[maxn][maxn];
double ans,l,r,mid,cost;
double dis[maxn];
queue <int> q;
struct edge{
int x,y,w,op,next;
double c;
}g[maxv];
void add(int x,int y,int w,double c)
{
g[++cnt].x=x; g[cnt].y=y; g[cnt].w=w; g[cnt].c=c;
g[cnt].next=ls[x]; g[cnt].op=cnt+1; ls[x]=cnt;
g[++cnt].x=y; g[cnt].y=x; g[cnt].w=0; g[cnt].c=-c;
g[cnt].next=ls[y]; g[cnt].op=cnt-1; ls[y]=cnt;
}
bool spfa()
{
while (!q.empty()) q.pop();
for (int i=s;i<=t;i++)
{
dis[i]=-inf;
vis[i]=0;
}
dis[s]=0;
q.push(s);
vis[s]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]<dis[x]+g[i].c))
{
dis[y]=dis[x]+g[i].c;
pre[y]=i;
if (!vis[y])
{
vis[y]=1;
q.push(y);
}
}
}
vis[x]=0;
}
if (dis[t]!=-inf) return 1;
return 0;
}
void mcf()
{
int i=t,x=1e9;
while (i!=s)
{
x=min(x,g[pre[i]].w);
i=g[pre[i]].x;
}
flow+=x;
i=t;
while (i!=s)
{
g[pre[i]].w-=x;
g[g[pre[i]].op].w+=x;
cost+=g[pre[i]].c*x;
i=g[pre[i]].x;
}
}
int check(double mid)
{
for (int i=1;i<=cnt;i+=2)
{
g[i].w=1;
g[i+1].w=0;
if ((g[i].x!=s) && (g[i].y!=t))
{
int u=g[i].x,v=g[i].y-n;
g[i].c=(double)a[u][v]-mid*b[u][v];
g[i+1].c=-g[i].c;
}
}
cost=0;
while (spfa()) mcf();
if (cost>=0) return 1;
return 0;
}
int main()
{
scanf("%d",&n);
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]);
}
s=0; t=2*n+1;
l=0; r=10000;
for (int i=1;i<=n;i++)
{
add(s,i,1,0);
add(i+n,t,1,0);
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
add(i,j+n,1,0);
}
}
while (r-l>1e-7)
{
mid=(l+r)/2;
if (check(mid)) ans=mid,l=mid;
else r=mid;
}
printf("%.6lf",ans);
}