[SDOI2017] 新生舞会

题意:

有n个男生和n个女生参加舞会,一个男生和一个女生互为舞伴。

已知第i个男生与第j个女生跳舞会产生$a_{i,j}$的喜悦度和$b_i,j$的不协调度。

你希望最终配对方案的$frac{sum{a_{i,j} }}{sum{b_{i,j} }}$最大,求这个最大值。

$nleq 100,1leq a_{i,j},b_{i,j}leq 10000$。

题解:

求一个最大化的比值基本就是分数规划了。

于是二分答案k,问题变为判断是否存在$sum{a_{i,j}-k imes b_{i,j}}geq 0$的方案。

显然直接跑个最大费用二分图匹配即可。

复杂度$O(msqrt{n} log{k})$。

套路:

  • 求一个最大/最小化的比值$ ightarrow$分数规划。

代码:

#include<bits/stdc++.h>
#define maxn 505
#define maxm 1000005
#define inf 0x7fffffff
#define eps 1e-10
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int n,nxt[maxm],hd[maxn],to[maxm],fl[maxm];
double A[maxn][maxn],B[maxn][maxn];
double cst[maxm],dis[maxn],Cost;
int inq[maxn],vis[maxn],cnt,S,T; 
queue<int> q;

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;
}

inline void addedge(int u,int v,int w,double c){
    to[++cnt]=v,fl[cnt]=w,cst[cnt]=c,nxt[cnt]=hd[u],hd[u]=cnt;
    to[++cnt]=u,fl[cnt]=0,cst[cnt]=-c,nxt[cnt]=hd[v],hd[v]=cnt;
}

inline bool spfa(){
    for(int i=1;i<=T;i++) dis[i]=2e9;
    q.push(S),inq[S]=1,dis[S]=0;
    while(!q.empty()){
        int u=q.front(); q.pop(),inq[u]=0;
        for(int i=hd[u];i;i=nxt[i]){
            int v=to[i];
            if(fl[i]>0 && dis[v]>dis[u]+cst[i]){
                dis[v]=dis[u]+cst[i];
                if(!inq[v]) q.push(v),inq[v]=1;
            }
        }
    }
    return dis[T]<2e9;
}

inline int dfs(int u,int flow){
    if(u==T) return flow;
    vis[u]=1; int sum=0;
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(vis[v] || fl[i]<=0 || dis[v]!=dis[u]+cst[i]) continue;
        int f=dfs(v,min(flow,fl[i]));
        flow-=f,sum+=f,Cost+=f*cst[i],fl[i]-=f,fl[i^1]+=f;
        if(!flow) break;
    }
    if(!sum) dis[u]=2e9;
    return sum;
}

inline double Dinic(){
    Cost=0;
    while(spfa()) memset(vis,0,sizeof(vis)),dfs(S,inf);
    return Cost;
}

inline bool check(double k){
    memset(hd,0,sizeof(hd)),cnt=1;
    for(int i=1;i<=n;i++)
        addedge(S,i,1,0),addedge(i+n,T,1,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            addedge(i,j+n,1,k*B[i][j]-A[i][j]);
    return Dinic()<eps;
}

int main(){
    n=read(),S=2*n+1,T=2*n+2;
    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,r=10000;
    while(l+eps<r){
        double mid=(l+r)/2.0;
        check(mid)?l=mid:r=mid;
    }
    printf("%.6lf
",l);
    return 0;
}
新生舞会
原文地址:https://www.cnblogs.com/YSFAC/p/13217350.html