二分图

定义

二分图又称作二部图,是图论中的一种特殊模型。设G=(V,E)是一个无向图,如果顶点V可分割为互不相交的子集(A,B),并且图中每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集\((i\in A,j\in B)\),则称图G为一个二分图。

就是说无向图G中所有点划分为两个集合,并且任意一条边上的两个点不能属于同一集合。

判断方法

在dfs的过程中进行染色,每条边上的两个点只能一个染0,一个染1,能满足要求即是二分图,不能则不是二分图,注意至少要有两个点。

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int f[N];
vector<int>g[N];
int n,m;//点数,边数
bool dfs(int x,int c){
    f[x]=c;
    int len=g[x].size(),to;
    for(int i=0;i<len;++i){
        to=g[x][i];
        if(f[to]==c)return false;
        else if(f[to]==-1) if(!dfs(to,c^1))return false;
    }
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    int a,b;
    for(int i=0;i<m;++i){
        scanf("%d%d",&a,&b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    //建图
    if(n==1){printf("Not a bipartite graph\n");return 0;}
    for(int i=1;i<=n;++i)f[i]=-1;
    bool flag=0;
    for(int i=1;i<=n;++i){
        if(f[i]==-1&&!dfs(i,0)){flag=1;printf("Not a bipartite graph\n");break;}
    }
    if(!flag)printf("It's a bipartite graph\n");
    return 0;
}

最大匹配

给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。

匈牙利算法

简单总结一下:
1.在一个循环中依次给每个男生找对象(打个比方)。
2.依次访问每个可能成功的女生。
3.如果这名女生是在本轮循环中第一次访问到并且也还没对象,那么匹配成功。
4.如果这名女生是在本轮循环中第一次访问到但是有对象男2了,那么我们可以试着(如果失败的话就为当前男生找其他女生,男2不受影响)为男2找其他对象,这样就又来到了第一步。
5.如果这名女生是第二次访问到,那么她跟当前男生肯定是没戏了。(如果接着访问,在程序中体现为无限递归)
6.如果循环结构中的男生匹配完所有可能的女生都失败了,那么他就落单了。

例题:矩阵游戏

思路
将行与列进行匹配,若某一行与某一列的交点为1,则该行与该列之间连有边,可以发现行交换与列交换并不会改变最大匹配数(其实就是交换两个点的顺序而已,整个图的结构没有变化),只要最大匹配数为n就有解。
参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=500;
vector<int>node[N];
int t,n,a;
int match[N],used[N];
bool dfs(int x){
    int to,len=node[x].size();
    for(int i=0;i<len;++i){
        to=node[x][i];
        if(!used[to]){
            used[to]=1;
            if(!match[to]||dfs(match[to])){
                match[to]=x;
                return true;
            }
        }
    }
    return false;
}
int ans;
int main(){
    scanf("%d",&t);
    while(t--){
        ans=0;
        memset(match,0,sizeof(match));
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            scanf("%d",&a);
            if(a){
                node[i].push_back(j+n);
            }
        }
        for(int i=1;i<=n;++i){
            memset(used,0,sizeof(used));
            if(dfs(i))++ans;
        }
        for(int i=1;i<=n;++i)
        node[i].clear();
        if(ans==n)
        printf("Yes\n");
        else printf("No\n");
    }
    
    return 0;
}

时间复杂度为\(O(nm)\)

posted @ 2022-03-22 20:55  何太狼  阅读(93)  评论(0编辑  收藏  举报