洛谷 P3386 二分图匹配模板

题目背景

二分图

感谢@一扶苏一 提供的hack数据

题目描述

给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数

输入输出格式

输入格式:

第一行,n,m,e

第二至e+1行,每行两个正整数u,v,表示u,v有一条连边

输出格式:

共一行,二分图最大匹配

输入输出样例

输入样例#1: 复制

1 1 1
1 1

输出样例#1: 复制

1

说明

n,m \leq 1000n,m≤1000, 1 \leq u \leq n1≤u≤n, 1 \leq v \leq m1≤v≤m

因为数据有坑,可能会遇到 v>mv>m 的情况。请把 v>mv>m 的数据自觉过滤掉。

算法:二分图匹配

今天又重新看了匈牙利算法的实现以及思想,突然恍然大悟。其实很早就接触了这个算法,但是一直没有太理解。今天又重新看了一遍,就明白了。所以说,如果看了好久还是感到很迷惑就先放到一边,等有一定积累的时候再回头看的时候很快就明白了。

算法思想:匈牙利算法的思想就是为当前点的所连节点的pre寻找增广路,当然这里的增广路不是网络流里边的增广路,这里百度百科上讲的很好。

求二分图最大匹配可以用最大流或者匈牙利算法。

最大匹配

给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配.

选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)

如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。

算法

求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。但是这个算法的复杂度为边数的指数级函数。因此,需要寻求一种更加高效的算法。

增广路的定义(也称增广轨或交错轨):

若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径.

由增广路的定义可以推出下述三个结论:

1-P的路径长度必定为奇数,第一条边和最后一条边都不属于M.

2-P经过取反操作可以得到一个更大的匹配M'.

3-M为G的最大匹配当且仅当不存在相对于M的增广路径.

以上内容来自百度百科。

就像这样,容易看出来从x3->y2->x1->y3.不属于与属于m的边交替出现,且起点和终点都是未盖点。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;i++)

vector<int>mp[1005];
bool vis[1005];
int pre[1005];

bool dfs(int now)
{
    for(int i=0;i<mp[now].size();i++)
    {
        int tmp=mp[now][i];
        if(!vis[tmp])
        {
            vis[tmp]=true;
            if(!pre[tmp]||dfs(pre[tmp]))
            {
                pre[tmp]=now;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    int n,m,e;
    scanf("%d%d%d",&n,&m,&e);
    rep(i,1,e)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(v>m)continue;
        mp[u].push_back(v);
    }
    int ans=0;
    rep(i,1,n)
    {
        memset(vis,false,sizeof(vis));
        if(dfs(i))ans++;
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/82762165