二分图匹配算法笔记

定义

给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
极大匹配(Maximal Matching)是指在当前已完成的匹配下,无法再通过增加未完成匹配的边的方式来增加匹配的边数。最大匹配(maximum matching)是所有极大匹配当中边数最大的一个匹配。选择这样的边数最大的子集称为图的最大匹配问题。
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
求二分图最大匹配可以用最大流(Maximal Flow)或者匈牙利算法(Hungarian Algorithm)

例题



棋盘建模:
【类型概述】:
有一种很经典的二分图模型,在一个n*n的矩阵中,这个矩阵里面有k个障碍物,你拥有一把武器,一发弹药一次能消灭一行或者一列的障碍物,求消灭全部障碍物所需的最少弹药数。
可以这样考虑:我们以所有行为二分图的左顶点,所有的列为右顶点,那么如果位于坐标p(x,y)有障碍物,我们就连一条边,然后我们只需要最少的顶点覆盖所有的边即可。这样就是二分图的最小顶点覆盖问题了,我们又知道最大小顶点等于二分图最大匹配。


1.HDU 1045
【题意】:
给出一张图,图中'X'表示wall,'.'表示空地,可以放置blockhouse同一条直线上只能有一个blockhouse,除非有wall隔开,问在给出的图中最多能放置多少个blockhous
【分析】:
把原始图分别按行和列缩点
建图:横竖分区。先看每一列,同一列相连的空地同时看成一个点,显然这样的区域不能够同时放两个点。这些点作为二分图的X部。同理在对所有的行用相同的方法缩点,作为Y部。 连边的条件是两个区域有相交部分(即'.'的地方)。最后求最大匹配就是答案。

#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define mod(x) ((x)%M)
//#define pi acos(-1.0)
#define rep(i,x,n) for(int i=(x); i<(n); i++)
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const int maxn = 1e2;
const double pi = acos(-1.0);//
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const ll M = 2147493647;
int n,m,k,id,x,y,match[maxn],X[maxn][maxn],Y[maxn][maxn],vis[maxn];
char mp[maxn][maxn];
vector<int>e[maxn];
bool dfs(int u)
{
    for(int i=0; i<e[u].size(); i++)
    {
        int v=e[u][i];
        if(!vis[v])
        {
            vis[v]=1;
            if(match[v]==-1 || dfs(match[v]))
            {
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}
int cal()
{
    int ans=0;
    ms(match,-1);
    for(int i=1;i<=x;i++)
    {
        ms(vis,0);
        ans+=dfs(i);
    }
    return ans;
}

int main()
{
    while(~scanf("%d",&n),n)
    {
         for(int i=0;i<n;i++)
            scanf("%s",mp[i]);
         ms(X,0);
         ms(Y,0);
         for(int i=0;i<maxn;i++) e[i].clear();

         x=1,y=1; //编号
         for(int i=0;i<n;i++)
         {
             for(int j=0;j<n;j++)
             {
                 if(mp[i][j]=='.'&& !X[i][j])
                 {
                     for(int k=j; mp[i][k]=='.'&&k<n; k++)
                             X[i][k]=x;
                     x++;
                 }
                 if(mp[i][j]=='.'&& !Y[i][j])
                 {
                      for(int k=i; mp[k][j]=='.'&&k<n; k++)
                              Y[k][j]=y;
                      y++;
                 }
             }
         }

         for(int i=0;i<n;i++)
             for(int j=0;j<n;j++)
             {
                 if(mp[i][j]=='.')
                    e[X[i][j]].push_back(Y[i][j]);
             }
        int ans=cal();
        printf("%d\n",ans);
    }
}
/*
4
.X..
....
XX..
....
2
XX
.X
3
.X.
X.X
.X.
3
...
.XX
.XX
4
....
....
....
....

5 1 5 2 4
*/

HDU 1083 匈牙利算法计数
【题意】:

题目大意: 一共有N个学生跟P门课程,一个学生可以任意选一 门或多门课,问是否达成:
1.每个学生选的都是不同的课(即不能有两个学生选同一门课)

2.每门课都有一个代表(即P门课都被成功选过)

输入为:

第一行一个T代表T组数据

P N(P课程数, N学生数)

接着P行:

第几行代表第几门课程,首先是一个数字k代表对这门课程感兴趣的同学的个数,接下来是k个对这门课程感兴趣同学的编号。

输出为:

若能满足上面两个要求这输出”YES”,否则为”NO”

注意:是课程匹配的学生,学生没课上没事.....

【分析】:匈牙利算法计数
二分图最大匹配,对课程—学生关系建立一个图,进行二分图的最大匹配,
如果最大匹配数==课程数,说明能够满足要求,否则不能。

#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define mod(x) ((x)%M)
#define pi acos(-1.0)
#define rep(i,x,n) for(int i=(x); i<(n); i++)
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const int maxn = 1e3;
const double pi = acos(-1.0);//
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const ll M = 2147493647;
int n,m,k,id,x,y,match[maxn],X[maxn][maxn],Y[maxn][maxn],vis[maxn];
char mp[maxn][maxn];
vector<int>e[maxn];
bool dfs(int u)
{
    for(int i=0; i<e[u].size(); i++)
    {
        int v=e[u][i];
        if(!vis[v])
        {
            vis[v]=1;
            if(match[v]==-1 || dfs(match[v]))
            {
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}
int cal()
{
    int ans=0;
    ms(match,-1);
    for(int i=1;i<=n;i++)
    {
        ms(vis,0);
        ans+=dfs(i);
    }
    return ans;
}

int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        for(int i=0;i<maxn;i++) e[i].clear(); //
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            while(x--)
            {
                scanf("%d",&y);
                e[i].push_back(y);
            }
        }
        int ans=cal();
        if(ans==n) puts("YES");
        else puts("NO");
    }
}
/*
2
3 3
3 1 2 3
2 1 2
1 1
3 3
2 1 3
2 1 3
1 1
YES
NO
*/

HDU 1083 BFS染色判定二分图/双向
【题意】:
有n个关系,他们之间某些人相互认识。这样的人有m对。 你需要把人分成2组,使得每组人内部之间是相互不认识的。
如果可以,就可以安排他们住宿了。安排住宿时,住在一个房间的两个人应该相互认识。 最多的能有多少个房间住宿的两个相互认识。
【分析】:判断是否为二分图
在我用二维矩阵存下点与点的关系后,我会给每一个点上色(0或者1),基本上是从第一个点开始,然后如果与它相连的点都会被染成与第一个点不一样的颜色, 但是如果在这些点里,有的已经被染过,那么就要判断是否和第一个点对应,若相同, 说明不可以分成二分图,若是之前没有赋过值,那么就先赋上,然后以该点为起始点,找与它相连的所有点,就这样一直递归下去,这就是染色法。

#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define mod(x) ((x)%M)
#define pi acos(-1.0)
#define rep(i,x,n) for(int i=(x); i<(n); i++)
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const int maxn = 1e3;
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const ll M = 2147493647;
int n,m,k,id,x,y,match[maxn],X[maxn][maxn],Y[maxn][maxn],vis[maxn];
char mp[maxn][maxn];
vector<int>e[maxn];
bool dfs(int u)
{
    for(int i=0; i<e[u].size(); i++)
    {
        int v=e[u][i];
        if(!vis[v])
        {
            vis[v]=1;
            if(match[v]==-1 || dfs(match[v]))
            {
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}
int cal()
{
    int ans=0;
    ms(match,-1);
    for(int i=1;i<=n;i++)
    {
        ms(vis,0);
        ans+=dfs(i);
    }
    return ans;
}
bool bfs()
{
    queue<int> q;
    ms(vis,0);
    vis[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<e[u].size();i++)
        {
            int v=e[u][i];
            if(vis[u]==vis[v]) return 0; //颜色一样,则不是二分图
            else if(!vis[v]) //未染色
            {
                vis[v]=-vis[u]; q.push(v); //染成反色并压队
            }
        }
    }
    return 1;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<maxn;i++) e[i].clear(); //
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            e[x].push_back(y);
            e[y].push_back(x); //双向
        }
        int ans=cal();
        if(!bfs()) puts("No");
        else printf("%d\n",ans/2);
    }
}
/*
4 4
1 2
1 3
1 4
2 3
6 5
1 2
1 3
1 4
2 5
3 6
No
3
*/

转载于:https://www.cnblogs.com/Roni-i/p/9514953.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值