其他分享
首页 > 其他分享> > 3739. 【TJOI2014】匹配(match)

3739. 【TJOI2014】匹配(match)

作者:互联网

Description

有 \(N\) 个单身的男孩和 \(N\) 个单身女孩,男孩 \(i\) 和女孩 \(j\) 在一起得到的幸福值为 \(H_{i,j}\) 。一个匹配即对这 \(N\) 个男孩女孩的安排:每个男孩恰好有一个女朋友, 每个女孩恰好有一个男朋友。

一个匹配的幸福值即这 \(N\) 对男女朋友的幸福值的和。经典的问题是计算幸福值最大的匹配,即完美匹配。然而完美匹配有时候并不唯一,你需要计算,对于所有的完美匹配,其交集是什么。

Solution

这题是经典的费用流/带权网络流,我写的是费用流。

先用最大费用最大流的模板求出完美匹配。

题目要求的交集就是指那些一定在完美匹配中的边,一种朴素的想法是枚举每条男孩女孩间的边,将它删去,然后再跑最大费用最大流,看两次的结果有无变化,若变小则说明删去的那条边是一定在残量网络中的。

但这种做法会超时,考虑优化,注意到在第一次跑完费用流后,此时每个男孩有且仅有一条边连向女孩,而其他的所有边可以确定不在交集中,因为已经至少有一种方法不走它们了。

那么我们把这些边找出来,将每条边一一删去,然后再跑费用流,可以将时间复杂度下降一个数量级。

Code

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100
#define inf 2147483647
using namespace std;
struct node
{
    int to,next,flow,val,head,flag;
}a[N*N*4];
deque<int> q;
int n,x,S,T,tot=1,ans,sum,dis[N<<1],c[N],cur[N<<1];
bool bj[N<<1],bz[N<<1],bb[N*N*4];
void add(int x,int y,int fl,int va)
{
    a[++tot].to=y;
    a[tot].flow=fl;
    a[tot].val=va;
    a[tot].next=a[x].head;
    a[x].head=tot;
}
bool check()
{
    for (int i=1;i<=2*n+2;++i)
        dis[i]=-inf,bj[i]=false;
    q.push_back(T);
    dis[T]=0;
    while (!q.empty())
    {
        int x=q.front();q.pop_front();
        for (int i=a[x].head;i;i=a[i].next)
        {
            if (bb[i]) continue;
            int y=a[i].to;
            if (a[i].flag) continue;
            if (a[i^1].flow&&dis[y]<dis[x]-a[i].val)
            {
                dis[y]=dis[x]-a[i].val;
                if (!bj[y])
                {
                    bj[y]=true;
                    if (q.empty()||dis[y]<dis[q.front()]) q.push_back(y);
                    else q.push_front(y);
                }
            }
        }
        bj[x]=false;
    }
    return dis[S]>-inf;
}
int calc(int now,int flow)
{
    if (now==T) return flow;
    bz[now]=true;
    int fw=0;
    for (int i=cur[now];i;i=a[i].next)
    {
        if (bb[i]) continue;
        int v=a[i].to;
        if (!bz[v]&&dis[now]==dis[v]+a[i].val&&a[i].flow)
        {
            int f=calc(v,min(flow,a[i].flow));
            if (f)
            {
                a[i].flow-=f;
                a[i^1].flow+=f;
                fw+=f;
                flow-=f;
                cur[now]=i;
                if (!flow) break;
            }
        }
    }
    return fw;
}
int fyl()
{
    int res=0;
    while (check())
    {
        bz[T]=true;
        while (bz[T])
        {
            for (int i=1;i<=2*n+2;++i)
                cur[i]=a[i].head,bz[i]=false;
            res+=dis[S]*calc(S,inf);
        }
    }
    return res;
}
void beback()
{
    for (int i=a[S].head;i;i=a[i].next)
        a[i].flow=1;
    for (int i=1;i<=n;++i)
    {
        for (int j=a[i].head;j;j=a[j].next)
        {
            if (a[j].to==S) a[j].flow=0;
            if (a[j].to>n&&a[j].to<=2*n) a[j].flow=1;
        }
    }
    for (int i=n+1;i<=2*n;++i)
    {
        for (int j=a[i].head;j;j=a[j].next)
        {
            if (a[j].to==T) a[j].flow=1;
            if (a[j].to<=n) a[j].flow=0;
        }
    }
    for (int i=a[T].head;i;i=a[i].next) 
        a[i].flow=0;
}
int main()
{
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=n;++j)
        {
            scanf("%d",&x);
            add(i,j+n,1,x);
            add(j+n,i,0,-x);
        }
    S=n*2+1;T=n*2+2;
    for (int i=1;i<=n;++i)
    {
        add(S,i,1,0);add(i,S,0,0);
        add(i+n,T,1,0);add(T,i+n,0,0);
    }
    ans=fyl();
    printf("%d\n",ans);
    sum=ans;
    for (int i=1;i<=n;++i)
    {
        for (int j=a[i].head;j;j=a[j].next)
        {
            if (a[j].to>n&&a[j].to<=2*n) 
            {
                if (!a[j].flow) 
                {
                    c[i]=j;
                    break;
                }
            }
        }
    }
    for (int i=1;i<=n;++i)
    {
        beback();
        bb[c[i]]=true;
        bb[c[i]^1]=true;
        ans=fyl();
        if (ans<sum) printf("%d %d\n",i,a[c[i]].to-n);
        bb[c[i]]=false;
        bb[c[i]^1]=false;
    }
    return 0;
}

标签:include,匹配,int,flow,男孩,3739,TJOI2014,now,match
来源: https://www.cnblogs.com/Livingston/p/15803429.html