[SDOI2017]新生舞会

题目大意:

有n个男生和n个女生跳舞。第i个男生和第j个女生组合会产生a[i][j]的喜悦程度和b[i][j]的不协调值。

现在你要找到一种方案,使喜悦程度总和与不协调值总和的比值最大。求这个比值。

解题思路:

分数规划问题。

即令\(\frac{\sum a}{\sum b}\)最大。

令其为C,则\(\sum a=C\sum b\)。

\(\sum a-C\sum b=0\)。

二分C,则若\(\sum a-C\sum b\geq 0\),则答案可行。

判断答案的可行性,发现这是个二分图带权匹配问题,跑最大费用最大流即可,两点之间的费用即为\(a[i][j]-C\times b[i][j]\)。

若费用大于等于0则可行。

C++ Code:

#include<bits/stdc++.h>
const int S=0,T=202;
int n,cnt;
int a[105][105],b[105][105],head[205],fl[205],pre[205],q[40004];
int from[99999],to[99999],nxt[99999],cap[99999];double cost[99999];
bool vis[205];
double dis[205];
inline int min(int a,int b){return a<b?a:b;}
void spfa(int&flow,double&C){
    for(dis[S]=0;;){
        for(int i=S+1;i<=T;++i)dis[i]=-1e17;
        memset(vis,0,sizeof vis);
        memset(fl,0x3f,sizeof fl);
        memset(pre,0,sizeof pre);
        vis[S]=1;
        int l=0,r=1;
        for(q[1]=S;l!=r;){
            int u=q[l=l%40000+1];
            vis[u]=false;
            for(int i=head[u];~i;i=nxt[i])
            if(cap[i]&&dis[to[i]]<dis[u]+cost[i]){
                dis[to[i]]=dis[u]+cost[i];
                fl[to[i]]=min(fl[u],cap[i]);
                pre[to[i]]=i;
                if(!vis[to[i]])
                vis[q[r=r%40000+1]=to[i]]=1;
            }
        }
        if(dis[T]<-1e16)return;
        flow+=fl[T];
        C+=dis[T]*fl[T];
        for(int i=T;i;i=from[pre[i]]){
            --cap[pre[i]];
            ++cap[pre[i]^1];
        }
    }
}
bool check(double k){
    cnt=1;
    for(int i=1;i<=n;++i){
        cap[++cnt]=1;
        cap[++cnt]=0;
        cap[++cnt]=1;
        cap[++cnt]=0;
        for(int j=1;j<=n;++j){
            cap[++cnt]=1;
            cost[cnt]=a[i][j]-k*b[i][j];
            cap[++cnt]=0;
            cost[cnt]=-cost[cnt-1];
        }
    }
    int flow=0;double C=0;
    spfa(flow,C);
    return C+1e-8>=0;
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d",&n);
    cnt=1;
    for(int i=1;i<=n;++i){
        from[++cnt]=S;to[cnt]=i;nxt[cnt]=head[S];head[S]=cnt;cost[cnt]=0;
        from[++cnt]=i;to[cnt]=S;nxt[cnt]=head[i];head[i]=cnt;cost[cnt]=0;
        from[++cnt]=i+n;to[cnt]=T;nxt[cnt]=head[i+n];head[i+n]=cnt;cost[cnt]=0;
        from[++cnt]=T;to[cnt]=i+n;nxt[cnt]=head[T];head[T]=cnt;cost[cnt]=0;
        for(int j=1;j<=n;++j){
            from[++cnt]=i;to[cnt]=j+n;nxt[cnt]=head[i];head[i]=cnt;
            from[++cnt]=j+n;to[cnt]=i;nxt[cnt]=head[j+n];head[j+n]=cnt;
        }
    }
    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]);
    double l=0,r=1e6,ans=0;
    while(l+1e-8<r){
        double mid=(l+r)/2;
        if(check(mid))l=mid+1e-8,ans=mid;
        else r=mid-1e-8;
    }
    printf("%.6f\n",ans);
    return 0;
}

 

  

 

posted @ 2018-09-07 15:46  Mrsrz  阅读(222)  评论(0编辑  收藏  举报