二分图:最大独立集&最大匹配&最小顶点覆盖

最大独立集问题

Given N sets of integers, remove some sets so that the remaining all sets are disjoint with one another. Find the optimal solution so that the number of sets remaining at the end is maximum. please explain your algorithm properly rather than pasting code .

分析

题目构造数学模型很简单,每一个集合看成一个点,如果两个集合中有相同的数字,则这两个集合对应的点相连接。最后会构成一个无向图,我们就是要求这个图的最大独立集。要求一个图的最大独立集就是求其补图的最大团。

最大团

这里介绍一下最大团

给定一个无向图G=(V,E),如果U属于E,且任意(u,v)属于U,且同时又属于E,则称U是G的完全子图。 G的完全子图U是G的最大团当且仅当U不包含在G的更大的完全子图中,即U就是最大的完全子图。

如下图:

二分图:最大独立集&最大匹配&最小顶点覆盖_第1张图片

此图的最大团有:

二分图:最大独立集&最大匹配&最小顶点覆盖_第2张图片二分图:最大独立集&最大匹配&最小顶点覆盖_第3张图片 二分图:最大独立集&最大匹配&最小顶点覆盖_第4张图片

那怎样求一个图的呢?朴素的搜索将会是n*2^n的时间复杂度,所以需要进行剪枝,并记录一些状态,即DP+DFS的思想。先把代码写出来。

#define SIZE 102
int mat[SIZE][SIZE];  /*图矩阵*/
int dp[SIZE];
int mx;
int stack[SIZE][SIZE];
void dfs(int N,int num,int step){

    if(num==0){
        if(step > mx){
            mx=step;
        }
        return ;
    }

    for(int i=0;i<num;i++){
        int k = stack[step][i];
        if(step+N-k<=mx) return ;
        if(step+dp[k]<=mx) return ;
        int cnt = 0;
        for(int j=i+1;j<num;j++)
            if(mat[k][stack[step][j]]){
                 stack[step+1][cnt++]=stack[step][j];
            }
        dfs(N,cnt,step+1);
    }
}

void run(int N){

    mx =0;
    for(int i=N-1;i>=0;i--){
        int sz =0;
        for(int j=i+1;j<N;j++)
            if(mat[i][j]) stack[1][sz++]=j;
        dfs(N,sz,1);
        dp[i]=mx;
    }
}

下面简单地说一下思路(节点下标是从0开始的):
dp[i]表示从i到n-1中的最大团的节点数。
枚举每一个节点,看这个节点与哪些编号大于它的节点相连,记录这些节点,然后递归地处理这些节点。。。
怎样去做剪枝那?
假如已经到x个节点在“团”里的状态,处理到了第k个节点,判断
x+n-k <= mx

x+dp[k] <= mx
两个条件。只要有一个成立,则没有必要继续下去了。

最大独立集

最大独立集就是其补图的最大团
poj1419是一个很直白的求最大独立集的问题,可以试着做做

二分图的最大独立集

如果一个图是二分图,那么它的最大独立集就是多项式时间可以解决的问题了 |最大独立集| = |V|-|最大匹配数|
证明:
设最大独立集数为U,最大匹配数为M,M覆盖的顶点集合为EM。
为了证明|U|=|V|-|M|,我们分两步证明|U|<=|V|-|M|和|U|>=|V|-|M|
1 先证明 |U|<=|V|-|M|
M中的两个端点是连接的,所有M中必有一个点不在|U|集合中,所以|M|<=|V|-|U|
2 再证明|U|>=|V|-|M|
假设(x,y)属于M
首先我们知道一定有|U|>=|V|-|EM|,那么我们将M集合中的一个端点放入U中可以吗?
假设存在(a,x),(b,y),(a,b)不在EM集合中
如果(a,b)连接,则有一个更大的匹配存在,矛盾
如果(a,b)不连接,a->x->y->b有一个新的增广路,因此有一个更大的匹配,矛盾
所以我们可以了解到取M中的一个端点放入U中肯定不会和U中的任何一个点相连,所以|U|>=|V|-|EM|+|M|=|V|-|M|
所以,|U|=|V|-|M|

二分图的最小顶点覆盖

定义:

寻找一个点集,使得图上任意一条边至少一个端点位于这个点集内部。二分图的|最小点集|=|最大匹配|

证明:
按照定义所说,就是最大匹配中的每个匹配的一个节点就是最小点集。
如果有一条边的两个端点都不在EM中,那最大匹配就会变成|M|+1,产生矛盾。所以又|最小点集|<=|最大匹配|
我们现在只看最大匹配M,如果最小点集小于M,那么肯定有边无法涉及到,因此|最小点集|>=|最大匹配|
所以有|最小点集|=|最大匹配|
对于一个一般图是NP Hard的问题,对于二分图可能就是多项式可解的问题咯

最小路径覆盖

定义:

一个有向无环图,要求用尽量少的不相交的简单路径覆盖所有的节点。

构图:

建立一个二分图,把原图中的所有节点分成两份(X集合为i,Y集合为i'),如果原来图中有i->j的有向边,则在二分图中建立i->j'的有向边。最终|最小路径覆盖|=|V|-|M|

证明:
二分图:最大独立集&最大匹配&最小顶点覆盖_第5张图片

上图中,对应左边的DAG构造了右边的二分图,可以找到二分图的一个最大匹配M:1->3' 3->4',那么M重的这两条匹配边怎样对应DAG中的路径的边呢?
使二分图的一条边对应于DAG中的一条有向边:1->3'对应于左图的1->3,这样DAG中的1就有一个后继结点(3回事1的唯一后继结点,因为二分图中的一个顶点之多关联一条边!),所以1不会成为DAG中的一条路径中的结尾顶点,同样,3->4'对应于左图的3->4,3也不会成为结尾顶点,那么原图中总共有4个顶点,减去2个有后继的顶点,还有两个顶点,即DAG路径的结尾顶点,每个即为顶点对应一个路径。二分图中寻找最大匹配M,就是找到了对应DAG中的非路径结尾顶点的最大数目,那么DAG中|V|-|M|就是DAG中结尾顶点的最小数目,即DAG的最小路径数目。
下面写一个二分图匹配的递归方法的代码:

#define SIZE 100

int mat[SIZE][SIZE];  /*图矩阵*/

int match1[SIZE];
int match2[SIZE];

int color[SIZE];

bool dfs(int N,int u){
    for(int i=0;i<N;i++)
        if(mat[u][i] && color[i]){
            color[i]=1;
            if(match2[i]==-1){
                match2[i]=u;
                match1[u]=i;
                return true;
            }
            else{
                bool flag = dfs(N,match2[i]);
                if(flag){
                    match2[i]=u;
                    match1[u]=i;
                    return true;
                }
            }
        }

    return false;
}

int maxMatch(int N){
    memset(match1,-1,sizeof(match1));
    memset(match2,-1,sizeof(match2));
    for(int i=0;i<N;i++){
        memset(color,0,sizeof(color));
        dfs(N,i);
    }
    int ret = 0;
    for(int i=0;i<N;i++)
        if(match1[i]!=-1) ++ret;
    return ret;
}

再写一个二分图最大匹配的非递归方法

#define SIZE 100

int mat[SIZE][SIZE]; /*图矩阵*/

int match1[SIZE];
int match2[SIZE];

int queue[SIZE];
int head,tail;

int pre[SIZE];

int maxMatch(int N){
    int ret = 0;
    memset(match1,-1,sizeof(match1));
    memset(match2,-1,sizeof(match2));

    for(int i=0;i<N;i++){
        memset(pre,-1,sizeof(pre));
        head = tail = 0;
        queue[tail++] = i;
        while(head < tail && match1[i]==-1){
            int u = queue[head++];
            for(int j =0;j<N&&match1[i]==-1;j++)
                if(mat[u][j] && pre[j]==-1){
                    queue[tail++] = match2[j];
                    pre[j]=u;
                    if(queue[tail-1]<0){
                        for(int t=j,k=0;t>=0;j=t){
                            match2[j]=k=pre[j];
                            t=match1[k];
                            match1[k]=j;
                        }
                    }
                }
        }
    }
}

3个重要结论:

最小点覆盖数: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。可以证明:最少的点(即覆盖数)=最大匹配数 最小路径覆盖=最小路径覆盖=|N|-最大匹配数 用尽量少的不相交简单路径覆盖有向无环图G的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。 二分图最大独立集=顶点数-二分图最大匹配 在N个点的图G中选出m个点,使这m个点两两之间没有边,求m最大值。 如果图G满足二分图条件,则可以用二分图匹配来做.最大独立集点数 = N - 最大匹配数。

二分图匹配(Hopcroft-Carp的算法)。
初始化:g[][]邻接矩阵
调用:res=MaxMatch();  Nx,Ny要初始化!!!
时间复杂大为 O(V^0.5 E)

适用于数据较大的二分匹配 
***********************************************/ 
const int MAXN=3001;
const int INF=1<<28;
int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;
int dx[MAXN],dy[MAXN],dis;
bool vst[MAXN];
bool searchP()
{
    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;i++)
        if(Mx[i]==-1)
        {
            Q.push(i);
            dx[i]=0;
        }  
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis)  break;
        for(int v=0;v<Ny;v++)
            if(g[u][v]&&dy[v]==-1)
            {
                dy[v]=dx[u]+1;
                if(My[v]==-1)  dis=dy[v];
                else
                {
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }    
            }    
    }  
    return dis!=INF;    
}    
bool DFS(int u)
{
    for(int v=0;v<Ny;v++)
       if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1)
       {
           vst[v]=1;
           if(My[v]!=-1&&dy[v]==dis) continue;
           if(My[v]==-1||DFS(My[v]))
           {
               My[v]=u;
               Mx[u]=v;
               return 1;
           }    
       }  
    return 0;  
}
int MaxMatch()
{
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())
    {
        memset(vst,0,sizeof(vst));
        for(int i=0;i<Nx;i++)
          if(Mx[i]==-1&&DFS(i))  res++;
    }
    return res;   
}    

你可能感兴趣的:(二分图:最大独立集&最大匹配&最小顶点覆盖)