它处资料:最大独立集问题

出处

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

最大团

这里介绍一下最大团

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

如下图:


此图的最大团有:

 

那怎样求一个图的呢?朴素的搜索将会是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|

证明:

上图中,对应左边的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;
                        }
                    }
                }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值