bzoj4819 洛谷3705 SDOI2017 新生舞会 分数规划 费用流

题意:

有n个男生和n个女生,其中第i个男生和第j个女生配对的数据分别为aij和bij,要求找到一种配对方案,使∑aij/∑bij最大

题解:

拿到题目一看这个样子就考虑分数规划,首先二分答案,看是否存在满足∑aij/∑bij>=mid的解,若存在就意味着还可能有更大的解。建边时将两个人的配对的权值改成aij-bij*mid,跑费用流,答案大于等于0说明还可能有更优解。

最后注意一下精度问题。最后吐槽一下,自带大常数的我成功地被卡常了QAQ,最后还是简单卡了卡常数才过掉的

代码:

#include <bits/stdc++.h>
using namespace std;
 
double eps=1e-8;
int n,st,ed,hed[204],cnt;
double ans,v[204],res,a[101][101],b[101][101];
int inq[50001],w[204],h,t,f[50000],q[50001];
struct node
{
    int next,from,to,c;
    double cost;
}e[50001];
void add(int from,int to,int c,double cost)
{
    ++cnt;
    e[cnt].from=from;
    e[cnt].to=to;
    e[cnt].c=c;
    e[cnt].cost=cost;
    e[cnt].next=hed[from];
    hed[from]=cnt;
    ++cnt;
    e[cnt].from=to;
    e[cnt].to=from;
    e[cnt].c=0;
    e[cnt].cost=-cost;
    e[cnt].next=hed[to];
    hed[to]=cnt;
}
void bfs()
{
    memset(w,0,sizeof(w));
    memset(v,-0x3f,sizeof(v));
    q[1]=st;
    h=1,t=2;
    w[st]=2e9;
    v[st]=0;
    while(h!=t)
    {
        int x=q[h];
        inq[x]=0;
        for(int i=hed[x];i;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].c&&v[y]-v[x]-e[i].cost<-eps)
            {
                w[y]=min(w[x],e[i].c);
                v[y]=v[x]+e[i].cost;
                f[y]=i;
                if(!inq[y])
                {
                    q[t++]=y;
                    inq[y]=1;
                }
            }
        }
        ++h;
    }
    for(int i=f[ed];i;i=f[e[i].from])
    {
        e[i].c-=w[ed];
        e[i^1].c+=w[ed];
    }
}
int check(double k)
{
    cnt=1;
    memset(hed,0,sizeof(hed));
    for(int i=1;i<=n;++i)
    {
        add(st,i,1,0);
        add(i+n,ed,1,0);
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            add(i,n+j,1,a[i][j]-k*b[i][j]);
        }
    }
    res=0.0;
    while(1)
    {
        bfs();
        if(w[ed]>eps)
        res+=(double)w[ed]*v[ed];
        else
        break;
        if(res<-eps)
        break;
    }
    if(res>=-eps)
    return 1;
    else
    return 0;
}
double find()
{
    double l=1,r=1e4,mid,ji=0;
    while(r-l>eps)
    {
        mid=(l+r)/2.0;
        if(check(mid))
        ji=l=mid;
        else
        r=mid;
    }
    return ji;
}
int main()
{
    scanf("%d",&n);
    st=n+n+1,ed=n+n+2;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    scanf("%lf",&a[i][j]);  
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    scanf("%lf",&b[i][j]);
    ans=find();
    printf("%.6lf\n",ans);
    return 0;
}


猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/79900886