二分图复习笔记

来源:互联网 发布:农村淘宝地址怎么改 编辑:程序博客网 时间:2024/05/18 19:20

概念

匹配:一个边的集合,任何两边都没有公共点。
最大匹配:匹配边数最多的匹配。
完美匹配:所有顶点都是匹配点。
最小点覆盖:用最少的点,让每条边都至少和其中一个点相关联。
最小边覆盖:用尽量少的不相交简单路径覆盖有向无环图G的所有顶点。
最大独立集:在n个点的图G中选出两两之间没有边的尽量多的m个点,这m个点所组成的集合。

关系

最小点覆盖=最大匹配数
最小边覆盖=顶点数-最小顶点覆盖(最大匹配)
顶点数=二分图中最大独立集+最小顶点覆盖(最大匹配)
最大独立集的顶点数量=所有顶点数-最小点覆盖
最大团中顶点数量 = 补图的最大独立集中顶点数量

例题

出处:http://blog.csdn.net/u012294939/article/details/43763005
(时间有限不知道这些题以后会不会做,在这先写上一句话题解)

HDU 1083&&POJ 1469

一道Hungary的裸题
这道题必须要用邻接矩阵,否则要MLE。

注意:

1、Hungary(mch[i])
2、vis[i]=1; 单指一边,不能写在for的外面

//MLE #include<cstdio>#include<cstring>using namespace std;//int to[M],nxt[M],head[PO],etot;bool vis[305],ed[105][305];int mch[305];int p,n; bool Hungary(int u){    //for(int i=head[u];i;i=nxt[i])    for(int i=1;i<=n;i++)    if(ed[u][i]){        if(vis[i]) continue;        vis[i]=1;        if(!mch[i]||Hungary(mch[i])){            mch[i]=u;            return 1;        }    }    return 0;}void init(){    memset(mch,0,sizeof(mch));    memset(ed,0,sizeof(ed));}int main(){    int T;    scanf("%d",&T);    while(T--){        init();        scanf("%d%d",&p,&n);        for(int i=1;i<=p;i++){//课程             int k;            scanf("%d",&k);            for(int j=1;j<=k;j++){                int stu;                scanf("%d",&stu);                ed[i][stu]=1;            }        }        bool MK=0;        for(int i=1;i<=p;i++){            memset(vis,0,sizeof(vis));            if(!Hungary(i)){                MK=1;break;            }        }        if(!MK) printf("YES\n");        else printf("NO\n");    }    return 0;}

HDU 1151/POJ1422 DAG的最小不相交路径覆盖

翻了好多篇博客就看懂了这篇:
http://www.cnblogs.com/icode-girl/p/5418461.html
用自己的语言简述一下:若一个点不是结尾节点,那么它在二分图中一定还连向了别的节点(也就是它和那个点形成了一个匹配)。在二分图中求最大匹配也就是求有多少个点不是结尾节点。
所以总点数-最大匹配数=有多少个点是结尾节点
所以可得那个更简短的公式:
DAG的最小路径覆盖数=DAG图中的节点数-相应二分图中的最大匹配数.

POJ 2594 DAG最小的可相交路径覆盖

解法:先用floyd若u可以到达v则在二分图上连接u->v。接下来的步骤就和上一题一样了(求最大匹配数)。
翻了很多篇博客都没有对于解法的解释证明。
在这里借用上一题的证明感性理解一下:
最大匹配数也就是看多少个节点不是结尾节点。在这里DAG中一个节点(u)能到达另一节点(v),那二分图上一定有边使它们(u->v)相连。因为没说一个节点只能被一条路径经过,所以u在与v相连后在DAG中就不会受限制了。(限制指:在DAG中u->u’直接相连但u’已匹配别的点因此u和u’不能匹配,因此u变成结尾节点。而这题中新的建二分图方式就可使u与v相匹配,而u就不会变成结尾节点了)。

HDU 1150&&POJ 1325 最小顶点覆盖

POJ 2446

做了这道题但是有个地方还没想通,先不放题解了

#include<cstdio>#include<cstring>using namespace std;const int N = 50;const int NN = 2024 + 10;const int M = 200000 + 10;int n,m,k;int a[N][N],head[NN],to[M],nxt[M],etot;bool vis[NN];int match[NN];int p(int i,int j){    return (i-1)*n+j;}void adde(int u,int v){    to[++etot]=v;    nxt[etot]=head[u];    head[u]=etot;}bool hungary(int u){    for(int i=head[u];i;i=nxt[i]){        int v=to[i];        if(vis[v]) continue;        vis[v]=1;        if(!match[v]||hungary(match[v])){            match[v]=u;return 1;        }     }    return 0;}void init(){    etot=0;    memset(head,0,sizeof(head));    memset(a,0,sizeof(a));    memset(match,0,sizeof(match));}int main(){    while(scanf("%d%d%d",&m,&n,&k)!=EOF){        init();        for(int i=1;i<=k;i++){            int x,y;            scanf("%d%d",&x,&y);//[y][x]            a[y][x]=1;        }        for(int i=1;i<=m;i++)        for(int j=1;j<=n;j++)        if(!a[i][j]){            if(!a[i-1][j]&&i-1>=1){                if((i+j)%2) adde(p(i,j),p(i-1,j));                else adde(p(i-1,j),p(i,j));            }            if(!a[i][j-1]&&j-1>=1){                if((i+j)%2) adde(p(i,j),p(i,j-1));                else adde(p(i,j-1),p(i,j));            }        }        int mch=0;        for(int i=1;i<=n*m;i++)        if(head[i]){            memset(vis,0,sizeof(vis));            if(hungary(i)) mch++;        }         if(mch*2==n*m-k) printf("YES");        else printf("NO");    }    return 0;}

HDU 2819

http://www.cnblogs.com/gj-Acit/archive/2013/08/17/3265502.html
感觉这篇证明构建二分图的超清晰!!
不过性质的证明我看了那么多题解还是没有懂……

原创粉丝点击