zoukankan      html  css  js  c++  java
  • 二分图匹配——p3386 p2071 p2319 p1129(矩阵游戏)

    ---恢复内容开始---

    二分图,就是给你一个图,可以将点分为两部分,每一部分的点都能唯一映射到另一个集合里,也就是有连边;

    注:以下转自 http://blog.csdn.net/dark_scope/article/details/8880547

    匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

    -------等等,看得头大?那么请看下面的版本:

     

    通过数代人的努力,你终于赶上了剩男剩女的大潮,假设你是一位光荣的新世纪媒人,在你的手上有N个剩男,M个剩女,每个人都可能对多名异性有好感(惊讶-_-||暂时不考虑特殊的性取向),如果一对男女互有好感,那么你就可以把这一对撮合在一起,现在让我们无视掉所有的单相思(好忧伤的感觉快哭了),你拥有的大概就是下面这样一张关系图,每一条连线都表示互有好感。

     

    本着救人一命,胜造七级浮屠的原则,你想要尽可能地撮合更多的情侣,匈牙利算法的工作模式会教你这样做:

    ===============================================================================

    一: 先试着给1号男生找妹子,发现第一个和他相连的1号女生还名花无主,got it,连上一条蓝线

     

    ===============================================================================

    二:接着给2号男生找妹子,发现第一个和他相连的2号女生名花无主,got it

     

    ===============================================================================

    三:接下来是3号男生,很遗憾1号女生已经有主了,怎么办呢?

    我们试着给之前1号女生匹配的男生(也就是1号男生)另外分配一个妹子。

     

    (黄色表示这条边被临时拆掉)

     

    与1号男生相连的第二个女生是2号女生,但是2号女生也有主了,怎么办呢?我们再试着给2号女生的原配(发火发火)重新找个妹子(注意这个步骤和上面是一样的,这是一个递归的过程)

     

     

    此时发现2号男生还能找到3号女生,那么之前的问题迎刃而解了,回溯回去

     

    2号男生可以找3号妹子~~~                  1号男生可以找2号妹子了~~~                3号男生可以找1号妹子

    所以第三步最后的结果就是:

     

    ===============================================================================

    四: 接下来是4号男生,很遗憾,按照第三步的节奏我们没法给4号男生腾出来一个妹子,我们实在是无能为力了……香吉士同学走好。

     

    ===============================================================================

    这就是匈牙利算法的流程,其中找妹子是个递归的过程,最最关键的字就是“腾”字

    其原则大概是:有机会上,没机会创造机会也要上

    这个讲的挺好的;

    这个是模板,但是邻接矩阵应用限制大;

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    int n,m,e;
    int edge[maxn][maxn];
    int cp[maxn];
    int ask[maxn];
    int ans;
    
    
    bool search(int x)
    {
        for(int i=1;i<=m;i++)
        {
            if(edge[x][i])
            {
                if(ask[i]) continue;
                ask[i]=1;
                if(!cp[i]||search(cp[i]))
                {
                    cp[i]=x;
                    return 1;
                }
            }
        }
        return 0;
    }
    void matching()
    {
        memset(cp,0,sizeof(cp));
        for(int i=1;i<=n;i++)
        {
            memset(ask,0,sizeof(ask));
            if(search(i))
            {
                ans++;
            }
        }
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&e);
        for(int i=1;i<=e;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x<=n&&y<=m)
            {
                edge[x][y]=1;
                //vis[y][x]=1;
            }
        }
        matching();
        printf("%d",ans);
        return 0;
    }

     
    P2071 座位安排

    还是前式链向星舒服

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=5010;
    int n,m;
    int pre[maxn*2],last[maxn],other[maxn*2],l;
    
    void add(int x,int y)
    {
        l++;
        pre[l]=last[x];
        last[x]=l;
        other[l]=y;
    }
    
    int ans,cp[maxn][5],ask[maxn][5];
    //int ask2[maxn],cp2[maxn];
    
    bool search(int x)
    {
        for(int p=last[x];p;p=pre[p])
        {
            int v=other[p];
            for(int i=1;i<=2;i++)
            {
                if(!ask[v][i])
                {
                    ask[v][i]=1;
                    if(!cp[v][i]||search(cp[v][i]))
                    {
                        cp[v][i]=x;
                        return 1;
                    }
                }
                /*if(!ask2[i])
                {
                    ask2[i]=1;
                    if(!cp2[i]||search(cp2[i]))
                    {
                        cp2[i]=x;
                        return 1;
                    }
                }*/
            }
        }
        return 0;
    }
    
    void matching()
    {
        memset(cp,0,sizeof(cp));
        for(int i=1;i<=2*n;i++)
        {
            memset(ask,0,sizeof(ask));
            //memset(ask2,0,sizeof(ask2));
            if(search(i))
            {
                ans++;
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=2*n;i++)
        {
            int x,s;
            scanf("%d",&s);
            scanf("%d",&x);
            add(i,x);
            add(i,s);
        }
        matching();
        printf("%d",ans);
        return 0;
    }

    这个匈牙利算法我个人的理解就是先匹配,然后再递归替换;

    P2319 [HNOI2006]超级英雄

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2010;
    int n,m;
    int pre[maxn],last[maxn],other[maxn],l;
    int cp[maxn],ask[maxn];
    void add(int x,int y)
    {
        l++;
        pre[l]=last[x];
        last[x]=l;
        other[l]=y;
    }
    int ans;
    int num[maxn];
    bool search(int x)
    {
        for(int p=last[x];p;p=pre[p])
        {
            int v=other[p];
            if(ask[v]) continue;
            ask[v]=1;
            if(!cp[v]||search(cp[v]))
            {
                cp[v]=x;
                num[x]=v;
                return 1;
            }
        }
        return 0;
    }
    
    void matching()
    {
        memset(cp,0,sizeof(cp));
        for(int i=1;i<=n;i++)
        {
            memset(ask,0,sizeof(ask));
            if(search(i))
            {
                ans++;
            }
            else break;
        }
    }
    
    int main()
    {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x!=y)
            {
                add(i,x+1);
                add(i,y+1);
            }
            else add(i,x+1);
        }
        matching();
        printf("%d
    ",ans);
        for(int i=1;i<=ans;i++)
        {
            printf("%d
    ",num[i]-1);
        }
        return 0;
    }

    这几道题都是大同小异;

     p1129矩阵游戏到是有点思维含量;

    问是否可行,其实可以看出,就是行与列的匹配

    如果g[i][j]==1,那么就给ij连一条边;

    如果每行和每列都能成功匹配就是有解;

    因为ij是1啊,如果这两个能匹配,这行和列都不能动,其他行只能找别的;

    根据互换总能换成符合题意的形式;

    (注意范围)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=400;
    int t;
    int n,g[maxn][maxn];
    int pre[maxn*maxn],last[maxn*maxn],other[maxn*maxn],l;
    void add(int x,int y)
    {
        l++;
        pre[l]=last[x];
        last[x]=l;
        other[l]=y;
    }
    int cp[maxn],ask[maxn];
    int ans;
    
    bool search(int x)
    {
        for(int p=last[x];p;p=pre[p])
        {
            int v=other[p];
            if(ask[v]) continue;
            ask[v]=1;
            if(!cp[v]||search(cp[v]))
            {
                cp[v]=x;
                return 1;
            }
        }
        return 0;
    }
    void matching()
    {
        memset(cp,0,sizeof(cp));
        for(int i=1;i<=n;i++)
        {
            memset(ask,0,sizeof(ask));
            if(search(i))
            {
                ans++;
            }
        }
    }
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
            l=0;ans=0;
            memset(last,0,sizeof(last));
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    scanf("%d",&g[i][j]);
                    if(g[i][j])
                    {
                        add(i,j);
                    }
                }
            }
            matching();
            if(ans==n) printf("Yes
    ");
            else printf("No
    ");
        }
        return 0;
    }
  • 相关阅读:
    智能指针和二叉树(2):资源的自动管理
    c++智能指针和二叉树(1): 图解层序遍历和逐层打印二叉树
    QLineEdit拾遗:数据的过滤、验证和补全
    为Qt视图中的文字添加彩虹渐变效果
    python3的变量作用域规则和nonlocal关键字
    三种方法为QLineEdit添加清除内容按钮
    配置CLion作为Qt5开发环境
    c++随机排序容器中的元素
    c++性能测试工具:google benchmark入门(一)
    shared_ptr和动态数组
  • 原文地址:https://www.cnblogs.com/WHFF521/p/11574149.html
Copyright © 2011-2022 走看看