[BZOJ4819][Sdoi2017]新生舞会
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
试题分析
题面是Smg?
分数规划通常是解决一类平均值极值问题的方法,设(F(x),G(x))为线性函数,求({ans=frac{F(x)}{G(x)} })。
其中F,G各项的系数(in [0,1])。 考虑将问题转化为二分,就是求是否存在(frac{F(x)}{G(x)} ge ans),移项得到(F(x)-ans imes G(x)ge 0)。
二分找到(leq 0)的点即可。
对于这道题来说就非常裸了,就是一个带负权的二分图匹配,费用流水过。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
#define LL long long
inline int read(){
int x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int INF = 1047483600;
const int MAXN = 300010;
int cos_a[101][101],cos_b[101][101];
int N; int Root[300001],Node[MAXN+1],C[MAXN+1];
double Cost[MAXN+1]; int Next[MAXN+1],cnt;
bool inq[MAXN+1]; double dis[MAXN+1];
int T,S;
inline bool BFS(){
deque<int> que; memset(inq,false,sizeof(inq));
for(int i=0;i<=T;i++) dis[i]=INF; dis[T]=0; que.push_back(T); inq[T]=true;
while(!que.empty()){
int k=que.front(); que.pop_front(); inq[k]=false;
for(int x=Root[k];x!=-1;x=Next[x]){
int v=Node[x];
if(C[x^1]>0&&dis[v]>dis[k]-Cost[x]){
dis[v]=dis[k]-Cost[x];
if(!inq[v]){
if(que.empty()||dis[que.front()]>=dis[v]) que.push_front(v);
else que.push_back(v);
inq[v]=true;
}
}
}
}
return dis[S]<INF;
}
double ret=0;
inline int DFS(int k,int t){
if(k==T) {inq[k]=true; return t;} inq[k]=true;
int res=0;
for(int x=Root[k];x!=-1;x=Next[x]){
int v=Node[x];
if(C[x]>0&&dis[v]==dis[k]-Cost[x]&&!inq[v]){
int tmp=DFS(v,min(t,C[x]));
ret+=1.0*Cost[x]*tmp; C[x]-=tmp; C[x^1]+=tmp;
res+=tmp; t-=tmp; if(!t) return res;
}
} return res;
}
inline void insert(int u,int v,int c,double w){
Node[cnt]=v; Next[cnt]=Root[u]; Cost[cnt]=w; C[cnt]=c; Root[u]=cnt++; return ;
}
inline bool check(double k){
memset(Root,-1,sizeof(Root)); cnt=0; ret=0;
S=0; T=2*N+1;
for(int i=1;i<=N;i++) {
insert(S,i,1,0); insert(i,S,0,0);
insert(i+N,T,1,0); insert(T,i+N,0,0);
}
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
insert(i,j+N,1,k*cos_b[i][j]-cos_a[i][j]);
insert(j+N,i,0,cos_a[i][j]-k*cos_b[i][j]);
}
} while(BFS()) DFS(S,INF);
return ret<=(1e-7);
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
N=read();
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++) cos_a[i][j]=read();
} for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++) cos_b[i][j]=read();
} double l=0.0,r=1e6,ans=0;
while(r-l>=1e-7){
double mid=(l+r)/2.0;
if(check(mid)) ans=mid,l=mid;
else r=mid;
} printf("%.6lf
",ans);
return 0;
}