• 二分图及最大匹配


    860. 染色法判定二分图

    给定一个 n

    个点 m

    条边的无向图,图中可能存在重边和自环。

    请你判断这个图是否是二分图。

    输入格式

    第一行包含两个整数 n

    和 m

    接下来 m

    行,每行包含两个整数 u 和 v,表示点 u 和点 v

    之间存在一条边。

    输出格式

    如果给定图是二分图,则输出 Yes,否则输出 No

    数据范围

    1≤n,m≤105

    输入样例:

    1. 4 4
    2. 1 3
    3. 1 4
    4. 2 3
    5. 2 4

    输出样例:

    Yes
    

     * 先将任意一个顶点染成一种颜色,再将相邻顶点染成另一种颜色,重复每一个顶点(对于
     * 接下来还没有染色的点(此点一定和之前染过色的顶点不是连通的),任意染成一种颜色,
     * 将相邻的顶点染成另外一种颜色,);如果对于每个顶点其相邻顶点的颜色都不相同,
     * 那么说明此图是二分图;

    1. /**
    2. * 先将任意一个顶点染成一种颜色,再将相邻顶点染成另一种颜色,重复每一个顶点(对于
    3. * 接下来还没有染色的点(此点一定和之前染过色的顶点不是连通的),任意染成一种颜色,
    4. * 将相邻的顶点染成另外一种颜色,);如果对于每个顶点其相邻顶点的颜色都不相同,
    5. * 那么说明此图是二分图;
    6. */
    7. #include <iostream>
    8. #include <algorithm>
    9. #include <vector>
    10. using namespace std;
    11. const int maxn = 1e5+10;
    12. vector<int> Adj[maxn];
    13. int color[maxn]; //每个顶点是否着色
    14. int Nv,Ne; //顶点数,边数
    15. void Read() //读入数据
    16. {
    17. cin >> Nv >> Ne;
    18. for(int i=0;i<Ne;++i)
    19. {
    20. int u,v;
    21. cin >> u >> v;
    22. Adj[u].push_back(v);
    23. Adj[v].push_back(u);
    24. }
    25. }
    26. int dfs(int u,int c) //对u这个顶点进行着色
    27. {
    28. color[u]=c;
    29. for(int i=0;i<Adj[u].size();++i)
    30. {
    31. int v=Adj[u][i];
    32. if(color[v]==0) //如果u的邻接点v还没有被着色
    33. {
    34. if(dfs(v,-c) == 0) //如果对v着色冲突,即与u着了相同的颜色
    35. return 0;
    36. }
    37. else if(color[v]==color[u]) //如果v已经被着色,但是与u着了相同的颜色
    38. return 0;
    39. }
    40. return 1; //没冲突
    41. }
    42. int is_bin_graph()
    43. {
    44. for(int i=1;i<=Nv;++i)
    45. {
    46. if(color[i] == 0)
    47. {
    48. if(dfs(i,1) == 0) //着色失败
    49. return 0;
    50. }
    51. }
    52. return 1; //着色成功,是二分图
    53. }
    54. int main()
    55. {
    56. Read();
    57. if(is_bin_graph() == 1)
    58. puts("Yes");
    59. else
    60. puts("No");
    61. return 0;
    62. }

    861. 二分图的最大匹配

    给定一个二分图,其中左半部包含 n1

    个点(编号 1∼n1),右半部包含 n2 个点(编号 1∼n2),二分图共包含 m

    条边。

    数据保证任意一条边的两个端点都不可能在同一部分中。

    请你求出二分图的最大匹配数。

    二分图的匹配:给定一个二分图 G

    ,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M

    是一个匹配。

    二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

    输入格式

    第一行包含三个整数 n1

    、 n2 和 m

    接下来 m

    行,每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v

    之间存在一条边。

    输出格式

    输出一个整数,表示二分图的最大匹配数。

    数据范围

    1≤n1,n2≤500

    ,
    1≤u≤n1,
    1≤v≤n2,
    1≤m≤105

    输入样例:

    1. 2 2 4
    2. 1 1
    3. 1 2
    4. 2 1
    5. 2 2

    输出样例:

    2
    

     
     * 虽然题目说每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v
     * 之间存在一条边。看着是无向边,但是处理起来,只能看作是单向边,因为是左部和右部
     * 相连,左部存在n1个顶点( 1,2,……,n1),右部存在n2个顶点(1,2,……,n2);
     * 处理成无向图,就不存在左部和右部之分,与题意不符合要求;

    /**
     * 对于还没有匹配到“对象”的顶点v,这简单,直接让u和v匹配在一起就完事儿了;
     * 或者已经匹配到对象的顶点v,但是想给v换一个“对象”,这就有点麻烦了,得问问
     * v的对象match[v],看他是否有其他中意的人,让他去找其他人,如果能找到,那就让
     * match[v]和他中意的那个人,而v就从现任变为前任,转而让u和v在一起;
     * 如果一直搜索完所有人,都不能使v与match[v]分开,那么注定u与v是有缘无份,不能够
     * 在一起;
     * 以上就是dfs完成的操作:
    */

    bool dfs(int u)
    {
        for(int v=1;v<=n2;++v)  //遍历右半部分的顶点
        {
            if(hs[v]==0 && G[u][v]==1) //v还未被访问
            {
                hs[v]=1;    //记录v未被访问
                if(match[v]==0 || dfs(match[v])) //v还未被匹配或者给v换一个对象能够成功
                {
                    match[v]=u; //因为是单向边,不是双向边,这一轮比较完以后,
                    return 1;   //下一轮左边并不会用到u这个数;
                }
            }
        }
            
        return 0;
    }

    int max_bin_graph()
    {
        int sum=0;
        for(int i=1;i<=n1;++i)
        {
            fill(hs,hs+maxn,0); //清除上一轮的标记
            if(dfs(i)==1)
                ++sum;
        }
        
        return sum;
                
    }

    1. /**
    2. * 虽然题目说每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v
    3. * 之间存在一条边。看着是无向边,但是处理起来,只能看作是单向边,因为是左部和右部
    4. * 相连,左部存在n1个顶点( 1,2,……,n1),右部存在n2个顶点(1,2,……,n2);
    5. * 处理成无向图,就不存在左部和右部之分,与题意不符合要求;
    6. */
    7. #include <iostream>
    8. #include <cstring>
    9. #include <algorithm>
    10. using namespace std;
    11. const int maxn = 510;
    12. int G[maxn][maxn]; //邻接矩阵
    13. int match[maxn]; //边匹配
    14. int hs[maxn]; //顶点是否被访问
    15. int n1,n2,m;
    16. void Read()
    17. {
    18. cin >> n1 >> n2 >> m;
    19. for(int i=0;i<m;++i)
    20. {
    21. int u,v;
    22. cin >> u >> v;
    23. G[u][v]=1;
    24. }
    25. }
    26. /**
    27. * 对于还没有匹配到“对象”的顶点v,这简单,直接让u和v匹配在一起就完事儿了;
    28. * 或者已经匹配到对象的顶点v,但是想给v换一个“对象”,这就有点麻烦了,得问问
    29. * v的对象match[v],看他是否有其他中意的人,让他去找其他人,如果能找到,那就让
    30. * match[v]和他中意的那个人,而v就从现任变为前任,转而让u和v在一起;
    31. * 如果一直搜索完所有人,都不能使v与match[v]分开,那么注定u与v是有缘无份,不能够
    32. * 在一起;
    33. * 以上就是dfs完成的操作:
    34. */
    35. bool dfs(int u)
    36. {
    37. for(int v=1;v<=n2;++v) //遍历右半部分的顶点
    38. {
    39. if(hs[v]==0 && G[u][v]==1) //v还未被访问
    40. {
    41. hs[v]=1; //记录v未被访问
    42. if(match[v]==0 || dfs(match[v])) //v还未被匹配或者给v换一个对象能够成功
    43. {
    44. match[v]=u; //因为是单向边,不是双向边,这一轮比较完以后,
    45. return 1; //下一轮左边并不会用到u这个数;
    46. }
    47. }
    48. }
    49. return 0;
    50. }
    51. int max_bin_graph()
    52. {
    53. int sum=0;
    54. for(int i=1;i<=n1;++i)
    55. {
    56. fill(hs,hs+maxn,0); //清除上一轮的标记
    57. if(dfs(i)==1)
    58. ++sum;
    59. }
    60. return sum;
    61. }
    62. int main()
    63. {
    64. Read();
    65. cout << max_bin_graph() << endl;
    66. return 0;
    67. }

    再增加一题:

    12095. 4.2-2完美的牛栏

    提高+/省选-

    题目详情

    题解

    测评详情

    农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术。不幸的是,由于工程问题,每个牛栏都不一样。第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来:每头奶牛都只愿意在她们喜欢的那些牛栏中产奶。上个星期,农夫约翰刚刚收集到了奶牛们的爱好的信息(每头奶牛喜欢在哪些牛栏产奶)。一个牛栏只能容纳一头奶牛,当然,一头奶牛只能在一个牛栏中产奶。
    给出奶牛们的爱好的信息,计算最大分配方案。

    输入格式:

    第一行 两个整数,N (0 <= N <= 200) 和 M (0 <= M <= 200) 。N 是农夫约翰的奶牛数量,M 是新牛棚的牛栏数量。
    
    
    
    第二行到第N+1行 一共 N 行,每行对应一只奶牛。第一个数字 (Si) 是这头奶牛愿意在其中产奶的牛栏的数目 (0 <= Si <= M)。后面的 Si 个数表示这些牛栏的编号。牛栏的编号限定在区间 (1..M) 中,在同一行,一个牛栏不会被列出两次。

    输出格式:

    只有一行。输出一个整数,表示最多能分配到的牛栏的数量。

    样例 1 :

    输入:
    5 5
    2 2 5
    3 2 3 4
    2 1 5
    3 1 2 5
    1 2
    输出:
    4

    照猫画虎:二分图的最大匹配

    1. #include <iostream>
    2. #include <algorithm>
    3. using namespace std;
    4. const int maxn = 210;
    5. int G[maxn][maxn];
    6. int match[maxn];
    7. bool hs[maxn];
    8. int n,m;
    9. void Read()
    10. {
    11. cin >> n >> m;
    12. for(int i=1;i<=n;++i)
    13. {
    14. int cnt;
    15. cin >> cnt;
    16. for(int j=0;j<cnt;++j)
    17. {
    18. int x;
    19. cin >>x;
    20. G[i][x]=1;
    21. }
    22. }
    23. }
    24. bool dfs(int u)
    25. {
    26. for(int v=1;v<=m;++v)
    27. if(hs[v]==0 && G[u][v]==1)
    28. {
    29. hs[v]=1;
    30. if(match[v]==0 || dfs(match[v]))
    31. {
    32. match[v]=u;
    33. return 1;
    34. }
    35. }
    36. return 0;
    37. }
    38. int max_bin_graph()
    39. {
    40. int sum=0;
    41. for(int i=1;i<=n;++i)
    42. {
    43. fill(hs,hs+maxn,0);
    44. if(dfs(i))
    45. ++sum;
    46. }
    47. return sum;
    48. }
    49. int main()
    50. {
    51. Read();
    52. cout << max_bin_graph() << endl;
    53. return 0;
    54. }

  • 相关阅读:
    剖析虚幻渲染体系(16)- 图形驱动的秘密
    【Linux】Centos 8 服务器部署:阿里云域名注册、域名解析、个人网站 ICP 备案详细教程
    Redis设计与实现(三)| 服务器与客户端的交互(事件IO模型)
    理解透彻API接口电商API接口有哪些?你需要一分钟看这篇文章
    uniapp 兼容pc与手机的样式方法
    【LeetCode力扣】287.寻找重复数(中等)
    wy的leetcode刷题记录_Day51
    Angular 基础
    机器学习之支持向量机(SVM)的求解方法
    机器学习强基计划3-2:详细推导支持向量机SVM原理+Python实现
  • 原文地址:https://blog.csdn.net/qq_51825761/article/details/126765012