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