【BZOJ4819】新生舞会(SDOI2017)-01分数规划+费用流

测试地址:新生舞会
做法:本题需要用到01分数规划+费用流。
首先看到题目中那个式子,就差不多能想到01分数规划了。按照套路处理:二分比值C,转化为判定性问题,即存不存在大于C的比值,把分母乘到另一边,再减回来,得到AiCBi>0这个式子,在这道题中,就是按边权为AijCBij建二分图,然后问有没有一个匹配的权值和大于0,那么我们当然是求最佳匹配,这就是费用流的经典应用之一了。
(但是实际测试中,分点测试的情况下貌似会被卡,更好的KM算法有待学习)
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const double eps=1e-8;
const double inf=1000000000.0;
int n,s,t,first[210],tot;
int hd,tl,q[8000010],last[210],laste[210];
double a[110][110],b[110][110],dis[210];
bool vis[210]={0};
struct edge
{
    int v,next,f;
    double c;
}e[50010];

void insert(int a,int b)
{
    e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
    e[++tot].v=a,e[tot].next=first[b],first[b]=tot;
}

void maxflow()
{
    for(int i=1;i<=2*n+2;i++)
        dis[i]=-inf;
    dis[s]=0.0;
    vis[s]=1;
    q[1]=s;
    hd=tl=1;
    while(hd<=tl)
    {
        int v=q[hd++];
        for(int i=first[v];i;i=e[i].next)
        {
            if (e[i].f&&dis[e[i].v]<dis[v]+e[i].c)
            {
                dis[e[i].v]=dis[v]+e[i].c;
                last[e[i].v]=v;laste[e[i].v]=i;
                if (!vis[e[i].v]) vis[e[i].v]=1,q[++tl]=e[i].v;
            }
        }
        vis[v]=0;
    }
}

bool check(double c)
{
    double ans=0.0;
    int step=0;

    tot=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            e[++tot].f=1,e[tot].c=a[i][j]-c*b[i][j];
            e[++tot].f=0,e[tot].c=c*b[i][j]-a[i][j];
        }
    for(int i=1;i<=n;i++)
    {
        e[++tot].f=1,e[tot].c=0.0;
        e[++tot].f=0,e[tot].c=0.0;
        e[++tot].f=1,e[tot].c=0.0;
        e[++tot].f=0,e[tot].c=0.0;
    }

    while(step<n)
    {
        maxflow();
        ans+=dis[t];
        step++;
        int x=t;
        while(x!=s)
        {
            e[laste[x]].f--;
            e[laste[x]^1].f++;
            x=last[x];
        }
    }
    return ans>eps;
}

int main()
{
    scanf("%d",&n);
    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]);

    tot=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            insert(i,n+j);
    s=2*n+1,t=2*n+2;
    for(int i=1;i<=n;i++)
    {
        insert(s,i);
        insert(n+i,t);
    }

    double l=0.0,r=10000.0;
    while(r-l>=eps)
    {
        double mid=(l+r)/2.0;
        if (check(mid)) l=mid;
        else r=mid;
    }
    printf("%.6lf",l);

    return 0;
}
posted @ 2018-04-10 15:37  Maxwei_wzj  阅读(80)  评论(0编辑  收藏  举报