4819: [Sdoi2017]新生舞会
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1239 Solved: 642
[Submit][Status][Discuss]
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
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
Sample Output
5.357143
HINT
Source
二分+费用流
首先二分答案mid 那么原问题的判断就变成了 a1+a2+a3+....+an>=mid*(b1+b2+b3+....+bn)
建图的话 i向j连一条ai-bj的边即可 最后跑费用流看一下最大费用是否大于等于0即可
/**************************************************************
Problem: 4819
User: zhangenming
Language: C++
Result: Accepted
Time:5648 ms
Memory:5296 kb
****************************************************************/
#include <bits/stdc++.h>
#define ll long long
#define eps 1e-7
#define inf 1e9+10
using namespace std;
inline int read(){
int x=0;int f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=1e5+10;
inline void print(double t){
printf("%.6lf\n",t);
}
struct node{
int y,next,f,back;
double w;
}e[MAXN];
int linkk[MAXN],len,n,m,a[110][110],b[110][110],vis[MAXN],s,t;
double dis[MAXN],ans;
inline void insert(int x,int y,int f,double w){
e[++len].y=y;e[len].next=linkk[x];linkk[x]=len;e[len].back=len+1;e[len].f=f;e[len].w=w;
e[++len].y=x;e[len].next=linkk[y];linkk[y]=len;e[len].back=len-1;e[len].f=0;e[len].w=-w;
}
inline bool spfa(){
deque<int> q;q.push_back(t);
for(int i=s;i<=t;i++) vis[i]=0,dis[i]=-inf;
vis[t]=1;dis[t]=0;
while(!q.empty()){
int tn=q.front();q.pop_front();
for(int i=linkk[tn];i;i=e[i].next){
if(e[e[i].back].f&&dis[e[i].y]<dis[tn]-e[i].w){
dis[e[i].y]=dis[tn]-e[i].w;
if(!vis[e[i].y]){
if(!q.empty()&&dis[e[i].y]>dis[q.front()]) q.push_front(e[i].y);
else q.push_back(e[i].y);
vis[e[i].y]=1;
}
}
}
vis[tn]=0;
}
//printf("%.6lf\n",dis[s]);
return dis[s]!=-inf;
}
inline int getcost(int x,int flow){
vis[x]=1;int f=0,d;
if(x==t) return flow;
for(int i=linkk[x];i;i=e[i].next){
if(e[i].f&&dis[e[i].y]==dis[x]-e[i].w&&!vis[e[i].y]){
if(d=getcost(e[i].y,min(flow-f,e[i].f))){
f+=d;e[i].f-=d;e[e[i].back].f+=d;
//cout<<d<<' ';print(e[i].w);
ans+=1.0*d*e[i].w;if(f==flow) return flow;
}
}
}
return f;
}
inline void zkw(){
while(spfa()){
vis[t]=1;
while(vis[t]){
for(int i=s;i<=t;i++) vis[i]=0;
getcost(s,inf);
}
}
}
inline bool solve(double mid){
//print(mid);
for(int i=s;i<=t;i++) linkk[i]=0;
len=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
insert(i,j+n,1,a[i][j]-1.0*mid*b[i][j]);
}
}
for(int i=1;i<=n;i++){
insert(s,i,1,0);insert(i+n,t,1,0);
}
ans=0;zkw();
if(ans>0) return true;
else return false;
}
int main(){
//freopen("All.in","r",stdin);
//freopen("zh.out","w",stdout);
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;double r=10010;
double ans1=0;
while(abs(r-l)>=eps){
double mid=(l+r)*0.5;
if(solve(mid)) ans1=max(ans1,mid),l=mid;
else r=mid;
}
printf("%.6lf\n",r);
return 0;
}