【BZOJ4819】新生舞会(分数规划,网络流)

【BZOJ4819】新生舞会(分数规划,网络流)

题面

BZOJ

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;
}

posted @ 2018-05-25 09:02  小蒟蒻yyb  阅读(232)  评论(0编辑  收藏  举报