[BZOJ4819]新生舞会

[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\times 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\n",ans);
    return 0;
}
posted @ 2018-08-11 21:31  wxjor  阅读(128)  评论(0编辑  收藏  举报