题解 P3386 【【模板】二分图匹配】

简介: 题目链接 首先呢声明一下,本宝宝发这篇题解只是为了(goto a;)个人还是比较喜欢跑dinic暴力跑最大流。。。竟然比匈牙利还快。。如果说不懂网络流的~~蒟蒻~~大佬们。可以看看这个(反正我就是在这篇文章看懂的)好啦,言归正传。

题目链接

首先呢声明一下,本宝宝发这篇题解只是为了(goto a;)

个人还是比较喜欢跑dinic暴力跑最大流。。。竟然比匈牙利还快。。
如果说不懂网络流的~~蒟蒻~~大佬们。
可以看看这个(反正我就是在这篇文章看懂的)

好啦,言归正传。

a:本宝宝想解释一下为什么这道题可以用网络流水过233....
首先我们看看二分图的概念。标准概念是:

简单的说,一个图被分成了两部分,相同的部分没有边,那这个图就是二分图,二分图是特殊的图。(摘自这里

如果你看不懂的话,那么请看某大佬给宝宝讲的时候的解释:
~~一群汉子和一群妹子匹配。没有基友也没百合,不能开后宫,这就是二分图。最大匹配就是求能组成的CP最多多少对~~  可能题解不过就是因为这句话233(逃~)

网络流最重要的是要建图!建图!建图!那么看看这道题怎么建图。
我们发现对于左边的n个点和右边的m个点。如果说最好的情况下。匹配的~~个~~对数是$min(m,n)$也就是说,我们左边尽可能的通过已有的边流到右半部分统计一下流到右半部分的容量最大值就是答案了(当然是要边的容量都是1的时候最简单了)。

所以我们将源点S设在左半部分左边,向左边所有的点连一条容量为1的边。汇点T设在右半部分的右边。从所有的右半部分的点连向汇点一条容量为1的边,暴力跑一边dicnic就好啦。

所以建边代码:

    scanf("%d%d%d",&n1,&m1,&e1);
    n=n1+m1+2;//源点编号为1,汇点编号为总点数+1
    for(int i=1;i<=n1;i++)
    {
        add(1,i+1,1);//空过源点,所以i+1,这里在连接源点和左部点。
        add(i+1,1,1);
    }
    for(int i=1;i<=e1;i++)
    {
        int u,v;//连已有的边
        scanf("%d%d",&u,&v);
        if(u<=n1&&v<=m1)
        add(u+1,v+n1+1,1),
        add(v+n1+1,u+1,1);
    }
    for(int i=1;i<=m1;i++)
    {
        add(i+n1+1,n,1);//连右部点和汇点
        add(n,i+n1+1,1);
    }

 


好啦完整代码奉上:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int cnt=2;
int alist[6000001];
struct data{
    int v;int next;int value;
}edge[6000001];
void add(int u,int v,int value)
{
    edge[cnt].v=v;
    edge[cnt].value=value;
    edge[cnt].next=alist[u];
    alist[u]=cnt++;
    return ;
}
int h[1000001];
int q[1000001];
//dicnic暴力参见上面提到的博客。
bool bfs()
{
    int x,next;
    memset(h,-1,sizeof(h));
    int head=0,tail=1;
    q[head]=1;
    h[1]=0;
    while(head<tail)
    {
        x=q[head++];
        next=alist[x];
        while(next)
        {
            int v=edge[next].v;
            int value=edge[next].value;
            if(value&&h[v]<0)
            {
                q[tail++]=v;
                h[v]=h[x]+1;
            }
            next=edge[next].next;
        }
    }
//    for(int i=1;i<=n*m;i++) printf("h[%d]=%d\n",i,h[i]);
    if(h[n]==-1) return false;
    return true;
}
int ans;
int dfs(int x,int y)
{
    if(x==n) return y;
    int next=alist[x];
    int w,used=0;
    while(next)
    {
        int v=edge[next].v;
        int value=edge[next].value;
        if(value&&h[v]==h[x]+1)
        {
                w=y-used;
                w=dfs(v,min(w,value));
                edge[next].value-=w;
                edge[next^1].value+=w;
                used+=w;
                if(used==y) return y;
        }
        next=edge[next].next;
    }
    if(!used) h[x]=-1;
    return used;
}
void dinic()
{
    while(bfs()) ans+=dfs(1,0x7fffffff);
}
int n1,m1,e1;
int main()
{
 //   freopen("testdata.in","r",stdin);
 //第一遍没A就是因为忘了删上面这句话。。。
    scanf("%d%d%d",&n1,&m1,&e1);
    n=n1+m1+2;
    for(int i=1;i<=n1;i++)
    {
        add(1,i+1,1);
        add(i+1,1,1);
    }
    for(int i=1;i<=e1;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(u<=n1&&v<=m1)
        add(u+1,v+n1+1,1),
        add(v+n1+1,u+1,1);
    }
    for(int i=1;i<=m1;i++)
    {
        add(i+n1+1,n,1);
        add(n,i+n1+1,1);
    }
    dinic();//暴力跑最大流
    printf("%d",ans);
    return 0;//程序拜拜
}

 


好啦这道题就先这样。对于要刷网络流的大佬们要是想练一练怎么见图的话P1402是一个很好的选择。
然后想深入学习的同学可以看看最小割和转对偶图。之后做一下P4001

相关文章
|
7月前
树状数组模板
树状数组模板
43 0
|
4月前
|
存储 算法 API
P4779 【模板】单源最短路径(标准版) 题解
本文提供了使用链式前向星存储结构和堆优化的Dijkstra算法模板,用以解决单源最短路径问题,并包含了完整的题解和代码示例。
P4779 【模板】单源最短路径(标准版) 题解
|
6月前
【LeetCode刷题】专题三:二分查找模板
【LeetCode刷题】专题三:二分查找模板
【LeetCode刷题】专题三:二分查找模板
|
7月前
|
机器人
[leedcode]刷题有感--动态规划入门及思路模板
[leedcode]刷题有感--动态规划入门及思路模板
|
7月前
[leetcode 数组]模版
[leetcode 数组]模版
|
7月前
[leetcode ~二叉树] 模版
[leetcode ~二叉树] 模版
|
机器学习/深度学习 设计模式 C++
C++模板元模板实战书籍讲解第一章(奇特的递归模板式以及小结和题目)
C++模板元模板实战书籍讲解第一章(奇特的递归模板式以及小结和题目)
86 0
|
算法
kmp算法模板
临近期末了,要开始复习了,先复习一下数据结构的kmp算法吧
|
编译器 C语言 C++
C++ 冒泡排序,模板
C++ 冒泡排序,模板