二分图算法模板以及相关知识

来源:互联网 发布:广州11选五遗漏数据 编辑:程序博客网 时间:2024/06/10 18:12

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

//板子, 直接套就行

/** @Cain*/const int maxn=1e3+5;bool vis[maxn];int link[maxn];  //初始化为0. 如果有0这个点, 那么就初始化为-1.vector<int>ve[maxn]; //关系矩阵,这样可以直接访问到相应的边.int n;bool Find(int x){    vis[x] = 1;    for(int i=0;i<ve[x].size();i++){        int m = ve[x][i];        if(vis[m]) continue;        vis[m] = true;        if(!link[m] || Find(link[m])){            link[m] = x;            link[x] = m;            return true;        }    }    return false;}

//这个代码的书写中最重要的就是注意下你的二分图中的点是放在一起的还是分成的两部分放的, 因为link和vis数组的使用都不一样了.

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

重要的一点就是看出来了用二分图做,然后就是建图了,再然后适当修改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代码:

int n,m;int link[1001];bool vis[1001];vector<int>data[1001];bool Find(int x){    vis[x] = 1;    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);    Fill(link,0);    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++)    {        Fill(vis,0);        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");    }}
原创粉丝点击