bzoj 4819: [Sdoi2017]新生舞会 二分答案+费用流

来源:互联网 发布:投弹兵升级数据 编辑:程序博客网 时间:2024/05/23 17:31

题意

有n对男女要配对,每对不同的男女会产生两个不同的权值a和b,求一种搭配方案使得(a’1+a’2+…+a’n)/(b’1+b’2+…+b’n)最大。
n<=100

分析

二分答案,设其为ans,则必然所有的(a’1+a’2+…+a’n)/(b’1+b’2+…+b’n)<=ans都满足,化简一下可以得到(a1-ans*b1)+(a2-ans*b2)+…+(an-ans*bn)<=0
那么将二分图每条边的权值变为a-ans*b然后看看最大费用是否不大于0即可。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=205;const double inf=100000000;const double eps=1e-8;int n,cnt,last[N],a[N][N],b[N][N],vis[N],s,t,pre[N],app[N];struct edge{int from,to,c,next;double w;}e[N*N];double dis[N],ans;queue <int> q;void addedge(int u,int v,int c,double w){    e[++cnt].from=u;e[cnt].to=v;e[cnt].c=c;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;    e[++cnt].from=v;e[cnt].to=u;e[cnt].c=0;e[cnt].w=-w;e[cnt].next=last[v];last[v]=cnt;}bool spfa(){    for (int i=s;i<=t;i++) dis[i]=-inf,app[i]=0;    dis[s]=0;vis[s]=1;    q.push(s);    while (!q.empty())    {        int u=q.front();        q.pop();        app[u]=1;        for (int i=last[u];i;i=e[i].next)            if (e[i].c&&dis[u]+e[i].w>dis[e[i].to])            {                dis[e[i].to]=dis[u]+e[i].w;                pre[e[i].to]=i;                if (!vis[e[i].to])                {                    vis[e[i].to]=1;                    q.push(e[i].to);                }            }        vis[u]=0;    }    if (!app[t]) return 0;    else return 1;}void mcf(){    int x=t,mn=inf;    while (pre[x])    {        mn=min(mn,e[pre[x]].c);        x=e[pre[x]].from;    }    ans+=mn*dis[t];    x=t;    while (pre[x])    {        e[pre[x]].c-=mn;        e[pre[x]^1].c+=mn;        x=e[pre[x]].from;    }}bool check(double mid){    cnt=1;    memset(last,0,sizeof(last));    for (int i=1;i<=n;i++) addedge(s,i,1,0);    for (int i=1;i<=n;i++) 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,a[i][j]-1.0*mid*b[i][j]);    ans=0;    while (spfa()) mcf();    if (ans<=0) return 1;    else return 0;}int main(){    scanf("%d",&n);    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=10000;    s=0;t=n*2+1;    while (r-l>=eps)    {        double mid=(l+r)/2;        if (check(mid)) r=mid;        else l=mid;    }    printf("%.6lf",l);    return 0;}
1 0