BZOJ3548-[ONTAK2010]Party-Bron_Kerbosch极大团

说在前面

这个算法
之前在WC听zzx讲过…(反正现在忘掉了hhhh)
然后刷题刷见了,所以去学了学=w=
(然而蠢如me并不会复杂度证明,只知道正确性和做法)


题目

BZOJ3548传送门

题目大意

现在有 n 个点,点与点间有两种关系(可以没有关系):

  1. 友好关系 P i , j :选 i 则必选 j
  2. 敌对关系 Q i , j :选 i 则不能选 j

询问最多可以选多少个点,以及选点最多的方案数
范围: n 250 n ( n 1 ) 3 | P | n ( n 1 ) 2 | Q | n ( n 1 ) 6

输入输出格式

输入格式:
第一行三个整数 n , | P | , | Q | ,其中 | P | 表示友好关系对数, | Q | 同理
接下来 | P | 行,每行两个数 u , v ,描述一对友好关系
再接下来 | Q | 行,格式同上,描述敌对关系

输出格式:
输出一行两个数,中间用空格隔开
第一个数字是最多选点,第二个数字是方案


解法

首先可以注意到,友好关系是一个很强的限制,选一个点就要把一个连通块全部选中
所以我们可以先对友好关系缩点,然后把那些内部有敌对关系的点从游戏中除外

然后再把敌对关系的边建出来,发现这个问题就转化成了:任意图的最大独立集
因为原图十分稠密,所以缩出来之后的点数很少(最多48个,这时有一堆孤立点以及一个完全图),就可以直接用Bron_Kerbosch算法求解了

关于学习资料,可以参考(当然还有wiki)
yefeng1627
yo_bc
DZYO

算法主要就是用两个集合来加速搜索且去重,然后用性质剪枝
关于为什么要么包含 u ,要么包含 N o n N ( u ) N ( u ) 是与 u 相邻的点) ,这里是一点me的理解:
首先 u 以及 N ( u ) 都是和已选所有点 有边的点
如果说最优方案中存在 N ( u ) 中的某个点,而不存在 u 。根据定义, u 和已选点,以及 N ( u ) 都有边,所以显然把 u 加入后方案更优,所以假设不成立
即:包含 N ( u ) 的最优方案 ,就要一定包含了 u ,所以我们不如直接枚举 u ,这样就节省了枚举 N ( u ) 的时间


下面是代码

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , P , Q ;
bool enemy[255][255] , mp[55][55] ;
struct Union_Set{
    int fa[255] ;
    void init(){
        for( int i = 1 ; i <= N ; i ++ ) fa[i] = i ;
    }
    int find( int x ){
        return ( fa[x] == x ? x : ( fa[x] = find( fa[x] ) ) ) ;
    }
    bool Union( int a , int b ){
        a = find( a ) , b = find( b ) ;
        if( a == b ) return false ;
        fa[a] = b ; return true ;
    }
} U ;

int siz[55] , some[55][55] , none[55][55] , ans , cnt ;
void dfs( int dep , int now , int scnt , int ncnt ){
    if( !scnt ){
        if( !ncnt ){
            if( now > ans ) ans = now , cnt = 1 ;
            else if( now == ans ) cnt ++ ;
        } return ;
    } int u = some[dep][1] ;
    for( int i = 1 ; i <= scnt ; i ++ ){
        int v = some[dep][i] , ns = 0 , nn = 0 ;
        if( mp[u][v] ) continue ;
        for( int j = 1 ; j < i ; j ++ )
            if( mp[u][ some[dep][j] ]/* BAN but not vis */ && mp[v][ some[dep][j] ] )
                some[dep+1][++ns] = some[dep][j] ;
        for( int j = i + 1 ; j <= scnt ; j ++ )
            if( mp[v][ some[dep][j] ] ) some[dep+1][++ns] = some[dep][j] ;
        for( int j = 1 ; j <= ncnt ; j ++ )
            if( mp[v][ none[dep][j] ] ) none[dep+1][++nn] = none[dep][j] ;
        dfs( dep + 1 , now + siz[v] , ns , nn ) ;
        none[dep][++ncnt] = v ;
    }
}

int id[255] , id_c ;
vector<int> B[255] ;
void solve(){
    for( int u = 1 ; u <= N ; u ++ )
        B[ U.find( u ) ].push_back( u ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        int siz = B[i].size() ; bool ok = ( siz != 0 ) ;
        for( int j = 0 ; j < siz && ok ; j ++ )
            for( int k = 0 ; k < siz && ok ; k ++ )
                if( enemy[ B[i][j] ][ B[i][k] ] ) ok = false ;
        if( !ok ) continue ;
        ::siz[++id_c] = siz ;
        for( int j = 0 ; j < siz ; j ++ ) id[ B[i][j] ] = id_c ;
    }
    // complements :
    memset( mp , true , sizeof( mp ) ) ;
    for( int i = 1 ; i <= N ; i ++ ) if( id[i] )
        for( int j = i ; j <= N ; j ++ ) if( id[j] )
            if( id[i] == id[j] || enemy[i][j] )
                mp[ id[i] ][ id[j] ] = mp[ id[j] ][ id[i] ] = false ;
    for( int i = 1 ; i <= id_c ; i ++ ) some[0][i] = i ;
    dfs( 0 , 0 , id_c , 0 ) ;
    printf( "%d %d" , ans , cnt ) ;
}

int main(){
    scanf( "%d%d%d" , &N , &P , &Q ) , U.init() ;
    for( int i = 1 , u , v ; i <= P ; i ++ )
        scanf( "%d%d" , &u , &v ) , U.Union( u , v ) ;
    for( int i = 1 , u , v ; i <= Q ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        enemy[u][v] = enemy[v][u] = true ;
    } solve() ;
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80156073