[Sdoi2017]新生舞会(分数规划+费用流)

题解:二分答案mid,然后将每个位置看成a-b*mid,然后由于是n个男生和n个女生匹配,每个人搭配一个cp,于是有点类似于https://www.lydsy.com/JudgeOnline/problem.php?id=1070(费用流模板题),加边(S,i,1,0),(i+n,T,1,0),(i,j+n,1,a[i][j]-mid*b[i][j])(注:括号内分别为始边、终边、流量、费用),跑最大费用最大流,若大于0则调整下界,否则调整上界,直至上下界基本重合。

#include<bits/stdc++.h>
using namespace std;
const int N=207;
struct edge{int u,v,w,nxt;double c;}e[N*N*4];
queue<int>q;
int n,S,T,ecnt,hd[N],vis[N],pre[N];
double a[N][N],b[N][N],d[N];
void adde(int x,int y,int z,double c)
{
    e[++ecnt]=(edge){x,y,z,hd[x],c},hd[x]=ecnt;
    e[++ecnt]=(edge){y,x,0,hd[y],-c},hd[y]=ecnt;
}
bool spfa()
{
    for(int i=S;i<=T;i++)d[i]=-1e18,pre[i]=0;
    d[S]=0,q.push(S);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=hd[u];i;i=e[i].nxt)
        if(e[i].w&&d[u]+e[i].c>d[e[i].v])
        {
            pre[e[i].v]=i,d[e[i].v]=d[u]+e[i].c;
            if(!vis[e[i].v])q.push(e[i].v),vis[e[i].v]=1;
        }
    }
    return d[T]>-1e18;
}
bool check()
{
    double ret=0;
    while(spfa())
    {
        int mn=1e9+7;
        for(int i=pre[T];i;i=pre[e[i^1].v])mn=min(mn,e[i].w);
        for(int i=pre[T];i;i=pre[e[i^1].v])e[i].w-=mn,e[i^1].w+=mn;
        ret+=mn*d[T];
    }
    return ret>0;
}
int main()
{
    scanf("%d",&n),T=2*n+1;
    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]);
    double l=0,r=10086,mid;
    while(r-l>1e-7)
    {
        mid=(l+r)/2;
        ecnt=1;
        memset(hd,0,sizeof hd);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        adde(i,j+n,1,a[i][j]-mid*b[i][j]);
        for(int i=1;i<=n;i++)adde(S,i,1,0),adde(i+n,T,1,0);
        if(check())l=mid;else r=mid;
    }
    printf("%.6lf",l);
}
View Code

猜你喜欢

转载自www.cnblogs.com/hfctf0210/p/10766182.html