二分图

作者: Anxdada | 来源:发表于2017-02-23 20:10 被阅读0次

    说说二分图,其实图论的题难点不在用算法,难在如何建图,只有图建好了,剩下的就简单了,在这说说求二分图的算法,即匈牙利算法,其实一点都不难,也很好理解拿笔写写就行了.

    二分图最大匹配----匈牙利算法

    重要的一点就是看出来了用二分图做,然后就是建图了,再然后适当修改Find函数就行了.

    int n,m;
    int link[1001];
    bool vis[1001];
    vector<int>data[1001];
    bool Find(int x)
    {
        for(int i=0;i<data[x].size();i++){
            int m=data[x][i];
            if(!vis[m]){
                vis[m] = true;
                if(!link[m] || Find(link[m])){
                    link[m] = x;
                    link[x] = m;
                    return true;
                }
            }
        }
        return false;
    }
    

    模板题
    AC代码:

    #include<cstdio>
    #include<cstring>
    #include<vector>
    using namespace std;
    #define CLR(x) memset(x,0,sizeof(x))
    int n,m;
    int link[1001];
    bool vis[1001];
    vector<int>data[1001];
    bool Find(int x)
    {
        for(int i=0;i<data[x].size();i++){
            int m=data[x][i];
            if(!vis[m]){
                vis[m] = true;
                if(!link[m] || Find(link[m])){
                    link[m] = x;
                    link[x] = m;
                    return true;
                }
            }
        }
        return false;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        CLR(link);
        int ans=0;
        for(int i=0;i<m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            data[u].push_back(v);
            data[v].push_back(u);
        }
        for(int i=1;i<=n;i++)
        {
            CLR(vis);
            if(!link[i] && Find(i))   //记住判断的先后逻辑顺序!!!
                ans++;
        }
        printf("%d\n", ans);
    }
    

    这里有些重要的定理,有许多题经过建图后发现就是求这些,故常常配合着这个二分图来运算需要记住!!!
    (通过一些小的改变即可达到要求)
    定理:
    定理1:最大匹配数M = 最小点覆盖数
    定理2:最大独立集 = 顶点数 - 最大匹配数
    定理3:有向图最小路径覆盖数 = 顶点数 - 最大匹配数
    定理4:无向图最小路径覆盖数 = 顶点数 - 最大匹配数/2
    (因为处理过两次)
    对以上名词的一些解释:
    最大匹配数:最大匹配的匹配边的数目
    最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
    最大独立集:选取最多的点,使任意所选两点均不相连
    最小路径覆盖数:对于一个 DAG (有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0 (即单个点).
    证明略.

    二分图判定----染色法
    模板题在此

    染色法判断是否是二分图.

    AC代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define CLR(x) memset(x,0,sizeof(x))
    const int maxn=1e4+5;
    int cas=1;
    bool flag;
    int n,m;
    bool vis[maxn][maxn];   //vis[i][j] 表示 i 到 j 是否相连过.是的话数组值为1,否则为 0 .
    vector<int>ve[maxn];
    int color[maxn];
    void dfs(int x,int col)
    {
        if(!flag) return ;   //flag=false, 后面就都没有必要再搜下去了.
        if(!color[x]) color[x]=col;  //如果该点没有被染色,就染上.
        else if(color[x]!=col){   //如果遇到将要染色的点不等于将要被染的色,则结束dfs,不是二分图.
            flag=false;
            return ;
        }
        for(int i=0;i<ve[x].size();i++)
        {
            int next=ve[x][i];
            if(!vis[x][next] && !vis[next][x]){
                vis[x][next]=1;
                dfs(next,3-col);
            }
        }
    }
    int main()
    {
        int t;
        cin >> t;
        while(t--){
           flag=true;
           scanf("%d %d",&n,&m);
           CLR(color);
           CLR(vis);
           for(int i=1;i<=n;i++)
               ve[i].clear();
           for(int i=0;i<m;i++){
                int u,v;
                scanf("%d %d",&u,&v);
                ve[u].push_back(v);
                ve[v].push_back(u);
            }
            for(int i=1;i<=n;i++)
            {
                if(!color[i]) dfs(i,1);    //循环染色.  分别左边染1,右边染2 .
            }
            if(flag) printf("Correct\n");
            else
                printf("Wrong\n");
        }
    }
    

    相关文章

      网友评论

          本文标题:二分图

          本文链接:https://www.haomeiwen.com/subject/gwsbwttx.html