说在前面
这个算法
之前在WC听zzx讲过…(反正现在忘掉了hhhh)
然后刷题刷见了,所以去学了学=w=
(然而蠢如me并不会复杂度证明,只知道正确性和做法)
题目
题目大意
现在有 个点,点与点间有两种关系(可以没有关系):
- 友好关系 :选 则必选
- 敌对关系 :选 则不能选
询问最多可以选多少个点,以及选点最多的方案数
范围:
,
,
输入输出格式
输入格式:
第一行三个整数
,其中
表示友好关系对数,
同理
接下来
行,每行两个数
,描述一对友好关系
再接下来
行,格式同上,描述敌对关系
输出格式:
输出一行两个数,中间用空格隔开
第一个数字是最多选点,第二个数字是方案
解法
首先可以注意到,友好关系是一个很强的限制,选一个点就要把一个连通块全部选中
所以我们可以先对友好关系缩点,然后把那些内部有敌对关系的点从游戏中除外
然后再把敌对关系的边建出来,发现这个问题就转化成了:任意图的最大独立集
因为原图十分稠密,所以缩出来之后的点数很少(最多48个,这时有一堆孤立点以及一个完全图),就可以直接用Bron_Kerbosch算法求解了
关于学习资料,可以参考(当然还有wiki)
yefeng1627
yo_bc
DZYO
算法主要就是用两个集合来加速搜索且去重,然后用性质剪枝
关于为什么要么包含
,要么包含
(
是与
相邻的点) ,这里是一点me的理解:
首先
以及
都是和已选所有点 有边的点
如果说最优方案中存在
中的某个点,而不存在
。根据定义,
和已选点,以及
都有边,所以显然把
加入后方案更优,所以假设不成立
即:包含
的最优方案 ,就要一定包含了
,所以我们不如直接枚举
,这样就节省了枚举
的时间
下面是代码
#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() ;
}