zoukankan      html  css  js  c++  java
  • 二分图匹配算法笔记

    定义

    给定一个二分图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
    ",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
    ",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
    */
    
  • 相关阅读:
    保持简单----纪念丹尼斯•里奇(Dennis Ritchie)
    转:有关retina和HiDPI那点事
    Powershell 学习
    Windows与Linux共享文件夹互相访问
    你知道C语言为什么会有“_”(下划线)吗?
    百度公共DNS
    AXIS2的一些认识
    待整理
    java复习汇总之面试篇
    落网歌曲图片下载
  • 原文地址:https://www.cnblogs.com/Roni-i/p/9514953.html
Copyright © 2011-2022 走看看