BZOJ 4819 [Sdoi2017]新生舞会 ——费用流 01分数规划

比值最大 分数规划

二分答案之后用费用流进行验证。

据说标称强行乘以1e7换成了整数的二分。

不过貌似实数二分也可以过。

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define mp make_pair
#define eps 1e-7
#define inf 1e9
#define maxn 50005
 
int h[maxn],to[maxn],ne[maxn],en=0,fl[maxn],n,S=maxn-2,T=maxn-1;
double cost[maxn],a[101][101],b[101][101],dis[maxn];
int with[maxn],minn[maxn],inq[maxn];
 
void add(int a,int b,double c,int d)
{
    to[en]=b;ne[en]=h[a];fl[en]=d;cost[en]=c; h[a]=en++;
    to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++;
}
 
queue <int> q;
 
bool SPFA()
{
    F(i,1,2*n) dis[i]=inf; dis[S]=inf; dis[T]=inf;
    memset(inq,0,sizeof inq);
    memset(with,0,sizeof with);
    memset(minn,0x3f,sizeof minn);
    q.push(S); inq[S]=1; dis[S]=0;
    while (!q.empty())
    {
        int x=q.front(); q.pop(); inq[x]=0;
        for (int i=h[x];i>=0;i=ne[i])
            if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0)
            {
                dis[to[i]]=dis[x]+cost[i];
                minn[to[i]]=min(minn[x],fl[i]);
                with[to[i]]=i;
                if (!inq[to[i]]) q.push(to[i]),inq[to[i]]=1;
            }
    }
    return dis[T]<inf-eps;
}
 
double zeng()
{
    for (int i=T;i!=S;i=to[with[i]^1])
    {
        fl[with[i]]-=minn[T];
        fl[with[i]^1]+=minn[T];
    }
    return minn[T]*dis[T];
}
 
double dinic()
{
    double ret=0,tmp;
    while (SPFA())
    {
        tmp=zeng();
        ret+=tmp;
    }
    return ret;
}
 
bool check(double d)
{
    memset(h,-1,sizeof h);
    en=0;
    F(i,1,n) add(S,i,0,1);
    F(i,1,n) add(i+n,T,0,1);
    F(i,1,n) F(j,1,n) add(i,j+n,d*b[i][j]-a[i][j],1);
    double ret=dinic();
    if (ret<0) return true;
    return false;
}
 
int main()
{
    scanf("%d",&n);
    F(i,1,n) F(j,1,n) scanf("%lf",&a[i][j]);
    F(i,1,n) F(j,1,n) scanf("%lf",&b[i][j]);
    double l=0,r=1e4;
    while (fabs(l-r)>eps)
    {
        double mid=(l+r)/2;
        if (check(mid)) l=mid;
        else r=mid;
    }
    printf("%.6lf\n",(l+r)/2);
}

  

posted @ 2017-04-20 11:00  SfailSth  阅读(188)  评论(0编辑  收藏  举报