zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    python实现JWT
    Flask上下文管理源码--亲自解析一下
    python小猪蹄儿
    AI数据分析(三)
    AI数据分析(二)
    AI数据分析(一)
    Linux目录结构
    Linux安装与基本命令
    Spring MVC基础环境搭建
    Windows 上解压缩版 MySQL 配置
  • 原文地址:https://www.cnblogs.com/Livingston/p/15803429.html
Copyright © 2011-2022 走看看