您的位置:首页 > 其它

最大独立集求解

2015-11-17 17:14 155 查看
定义:

独立集:在一个图中,找到一个集合包含的所有点相互之间都不存在连边

最大独立集:在所有独立集中包含元素个数最多的独立集

之前只是知道二分图的最大独立集 = 总点数-最大匹配数

但是一般无向图的情况下求解就不能这样了

换个角度思考,其实求最大独立集也是相当于建立一个相反图(把当前的边都去掉,添加上之前不被选中的边)

就变成求修改之后的最大完全图的点的个数了,因为我们要保证选到的点之间不存在任何相连的边,那么图反过来之后,选到的点两两之间就必然

存在边,否则说明之前的图是存在边的,也就是两个点不能全选中,而这样得到的就是完全图

而我们求点数最多的那个,也就是最大完全图,也即最大团

最近学习了一下这类问题的求解方式,这可以看做是一类搜索问题,不断dfs搜索找到最优解

这样很容易看出 这是一个NP问题,复杂度也是 O(2^n)的

所以优秀的剪枝是非常必要的

定义dp[i] 是 i ~ N 这些点所能构成的最大团的点数

那么我们就可以倒过来逐个算出dp[]值

对于前面的dp[]值就要利用之前算出来的值来剪枝

我们这里一个个从小到大添加节点,保证从当前出发添加进来的节点dp值已经求得

例如当前添加了 v , 之前有了 t 个点了

那么就可以用

t+dp[v] <=mx

t+N-t+1<=mx

来剪枝了(这个仔细想一下就知道了)

我们用一个_stack[][]数组记录能够扩展的节点,也就是这个数组中的点和之前取到的点每一个都存在连边,所以添加进来可以直接构成完全图

我们只要每次再添加节点后更新这个数组就可以了

因为是递归求解了,防止更新了会在回溯时出错,那么就讲数组定成两维的,第一维表示当前集合最大的点就可以直接用了

int cnt = 0;
for(int j=i+1 ; j<num ; j++){
  if(!mp[v][_stack[u][j]]) _stack[v][cnt++] = _stack[u][j];
}

然后POJ1419就是一道这个的裸题,只是每次记录了最优解要取到的点而已

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 105
bool mp

;
int T , n , k;
int dp
, ans
, tmp
, ret , mx , cnt;
int _stack

;

void build(int k)
{
memset(mp , 0 , sizeof(mp));
for(int i=1 ; i<=n ; i++) mp[i][i] = true;
for(int i=0 ; i<k ; i++){
int a , b;
scanf("%d%d" , &a , &b);
mp[a][b] = true;
}
}

void dfs(int u , int num , int step)
{
if(num == 0){
if(mx<step){
mx = step;
if(step>ret) for(int i=1 ; i<=step ; i++) ans[i] = tmp[i];
}
return;
}

for(int i=0 ; i<num ; i++){
int v = _stack[u][i];
//two methods of cut the node
if(step+dp[v]<=mx) continue;
if(step+n-v+1<=mx) continue;
//从stack中能访问到v说明,v和之前所有点都有连边的,只要重新更新stack中的数据就可以了
int cnt = 0;
for(int j=i+1 ; j<num ; j++){
if(!mp[v][_stack[u][j]]) _stack[v][cnt++] = _stack[u][j];
}
tmp[step+1] = v;
dfs(v , cnt , step+1);
}
}

int main()
{
//  freopen("a.in" , "r" , stdin);
scanf("%d" , &T);
while(T--){
scanf("%d%d" , &n , &k);
build(k);
ret = 0; //init
for(int i=n ; i>=1 ; i--){
cnt = 0 , mx = 1;
for(int j=i+1 ; j<=n ; j++){
if(!mp[i][j]) _stack[i][cnt++] = j;
}
tmp[1] = i;
dfs(i , cnt , 1);
dp[i] = mx;
ret = max(ret , dp[i]);
}
printf("%d\n" , ret);
for(int i=1 ; i<=ret ; i++){
if(i<ret) printf("%d " , ans[i]);
else printf("%d\n" , ans[i]);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: