[SDOI2017] 新生舞会——二分 最大费用最大流
[SDOI2017] 新生舞会
题目描述
学校组织了一次新生舞会,Cathy 作为经验丰富的老学姐,负责为同学们安排舞伴。
有 \(n\) 个男生和 \(n\) 个女生参加舞会,一个男生和一个女生一起跳舞,互为舞伴。
Cathy 收集了这些同学之间的关系,比如两个人之前认识没,计算得出 \(a_{i,j}\)。
Cathy 还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 \(b_{i,j}\),表示第 \(i\) 个男生和第 \(j\) 个女生一起跳舞时的不协调程度。
当然,还需要考虑很多其他问题。
Cathy 想先用一个程序通过 \(a_{i,j}\) 和 \(b_{i,j}\) 求出一种方案,再手动对方案进行微调。
Cathy 找到你,希望你帮她写那个程序。
一个方案中有 n 对舞伴,假设每对舞伴的喜悦程度分别是 \(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}\)
Cathy 希望 \(C\) 值最大。
输入格式
第一行一个整数 \(n\)。
接下来 \(n\) 行,每行 \(n\) 个整数,第 \(i\) 行第 \(j\) 个数表示 \(a_{i,j}\)。
接下来 \(n\) 行,每行 \(n\) 个整数,第 \(i\) 行第 \(j\) 个数表示 \(b_{i,j}\)。
输出格式
一行一个数,表示 \(C\) 的最大值。四舍五入保留 \(6\) 位小数,选手输出的小数需要与标准输出相等。
样例 #1
样例输入 #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\le n\le 5\)
对于 40% 的数据,\(1\le n\le 18\)
另有 20% 的数据,\(b_{i,j}\le 1\)
对于 100% 的数据,\(1\le n\le 100,1\le a_{i,j},b_{i,j}\le10^4\)
分析
二分C值,用最大费用最大流检查是否满足要求即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
long long INF=0x7fffffff;
struct edge{int y,n,x,z;double sp;}e[N<<1],t[N<<1];
double eps=0.00000001;
int vis[N],dfn,q[N],pre[N];
int n,sta,edn;
int head[N],cnt=1;
int a[210][210],b[210][210];
double dis[N];
void ad(int x,int y,int z)
{
e[++cnt].n=head[x];
e[cnt].y=y;
e[cnt].x=x;
e[cnt].z=z;
head[x]=cnt;
}
void init()
{
INF*=INF;
scanf("%d",&n);
sta=n*2+1;
edn=n*2+2;
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]);
for(int i=1;i<=n;++i)
{
ad(sta,i,1);
ad(i,sta,0);
ad(i+n,edn,1);
ad(edn,i+n,0);
for(int j=1;j<=n;++j)
{
ad(i,j+n,1);
ad(j+n,i,0);
}
}
}
void ISAP()
{
dis[0]=-INF;
for(int i=1;i<=edn;++i)
dis[i]=-INF;
dis[sta]=0;
++dfn;
int he=1,ta=0;
q[++ta]=sta;
while(he<=ta)//break the limit
{
int u=q[he++];
vis[u]=dfn-1;
for(int i=head[u];i;i=e[i].n)
{
int v=e[i].y;
if(t[i].z>0 && dis[v]<dis[u]+t[i].sp)
{
dis[v]=dis[u]+t[i].sp;
pre[v]=i;
if(vis[v]!=dfn)
{
q[++ta]=v;
vis[v]=dfn;
}
}
}
}
}
double returnflow()
{
int u=edn,mxflow=114514;
double spe=0;
while(u!=sta)
{
mxflow=min(mxflow,t[pre[u]].z);
u=e[pre[u]].x;
}
u=edn;
while(u!=sta)
{
spe+=t[pre[u]].sp*mxflow;
t[pre[u]].z-=mxflow;
t[pre[u]^1].z+=mxflow;
u=e[pre[u]].x;
}
return spe;
}
bool check(double lim)
{
for(int i=2;i<=cnt;i+=2)
{
t[i].z=e[i].z;
t[i^1].z=e[i^1].z;
int x=e[i].x,y=e[i].y;
if(x>n && x<=n*2)x-=n;
if(y>n && y<=n*2)y-=n;
double num=1.0*a[x][y]-1.0*lim*b[x][y];
t[i].sp=num;
t[i^1].sp=-num;
}
double mxflow=0;
while(1)
{
ISAP();
if(dis[edn]==dis[0])break;
mxflow+=returnflow();
}
return mxflow>=0;
}
void work()
{
double L=0,R=1e8+1000,mid=0,ans=-1;
while(R-L>=eps)
{
mid=(L+R)/2;
if(check(mid)){ans=mid;L=mid;}
else R=mid;
}
printf("%.6lf",ans);//too small
}
int main()
{
init();
work();
return 0;
}
本文来自博客园,作者:Glowingfire,转载请注明原文链接:https://www.cnblogs.com/Glowingfire/p/18462486