【BZOJ4819】新生舞会(分数规划,网络流)
题面
Description
学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。有n个男生和n个女生参加舞会
买一个男生和一个女生一起跳舞,互为舞伴。Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出
a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度。Cathy还需要考虑两个人一起跳舞是否方便,
比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度。当然,
还需要考虑很多其他问题。Cathy想先用一个程序通过a[i][j]和b[i][j]求出一种方案,再手动对方案进行微调。C
athy找到你,希望你帮她写那个程序。一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是a'1,a'2,...,a'n,
假设每对舞伴的不协调程度分别是b'1,b'2,...,b'n。令
C=(a'1+a'2+...+a'n)/(b'1+b'2+...+b'n),Cathy希望C值最大。
Input
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示a[i][j]。
接下来n行,每行n个整数,第i行第j个数表示b[i][j]。
1<=n<=100,1<=a[i][j],b[i][j]<=10^4
Output
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等
Sample Input
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
Sample Output
5.357143
题解
很明显的分数规划了
二分一个答案
现在要让\(\sum a-mid·\sum b\geq 0\)
显然是要求二分图的最大带权匹配
\(KM\)算法早就不会了
直接上费用流就行了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 111
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Line{int v,next,w;double fy;}e[MAX*MAX*2];
int h[MAX<<1],cnt;
inline void Add(int u,int v,int w,double fy)
{
e[cnt]=(Line){v,h[u],w,fy};h[u]=cnt++;
e[cnt]=(Line){u,h[v],0,-fy};h[v]=cnt++;
}
double dis[MAX<<1],Cost;
bool vis[MAX<<1];
int n,S,T,pe[MAX<<1],pv[MAX<<1];
int a[MAX][MAX],b[MAX][MAX];
bool SPFA()
{
queue<int> Q;Q.push(S);
for(int i=S;i<=T;++i)dis[i]=-1e18;
dis[S]=0;vis[S]=true;
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;
if(e[i].w&&dis[v]<dis[u]+e[i].fy)
{
dis[v]=dis[u]+e[i].fy;
pe[v]=i;pv[v]=u;
if(!vis[v])vis[v]=true,Q.push(v);
}
}
vis[u]=false;
}
if(dis[T]<=-1e18)return false;
for(int i=T;i!=S;i=pv[i])e[pe[i]].w--,e[pe[i]^1].w++;
Cost+=dis[T];
return true;
}
void Build(double mid)
{
for(int i=S;i<=T;++i)h[i]=0;cnt=2;
for(int i=1;i<=n;++i)Add(S,i,1,0);
for(int i=1;i<=n;++i)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,1.0*a[i][j]-mid*b[i][j]);
Cost=0;
}
bool check(double mid)
{
Build(mid);
while(SPFA());
return Cost>=0;
}
int main()
{
n=read();S=0;T=n+n+1;
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();
double l=0,r=1e4;
while(r-l>1e-7)
{
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%.6lf\n",l);
return 0;
}