poj 2942 Knights of the Round Table(点双连通)
题意:亚瑟王座下有N个骑士,他们要坐在一张圆桌上议会,但是你知道,骑士的脾气总是很暴躁的,如果相邻的两个骑士的意见不同,他们就会动用武力,亚瑟王为了使议会顺利进行,要求巫师梅林设计一种方案,使任意两个互相讨厌的骑士都不坐在一起,圆桌上的人必须是奇数个,因为他也许会为一个问题进行投票,不希望出现票数相等的情况,当然也不能只有一个人,因为一个人根本不用开什么会议嘛,给出互相讨厌的骑士的连接关系,最后不能参加会议的骑士就会被开除,现在亚瑟王想知道最少要开除多少骑士。
思路:建图很简单,就是给出的连接图的补图,求强连通分量也很容易想到,以为任意个强连通分量中的两个人都不是互相讨厌的,但是对于人数必须的奇数个,这就有点难想到了,我也只是想到了求强连通分支这个地方,后面的参考了一下解题报告,有个定理是:点双连通分量中存在奇圈,那么这个分量中所有的点都可以出现在奇圈上,这样的话,在求完强连通分支后,就在每个分支中找是否有奇环,如果有的话那么这个分支中的所有人都可以参加会议。至于求奇环,也是参照报告中的二分图的染色来求的。
代码:
View Code
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> #include <stack> #include <math.h> #define N 1005 using namespace std ; bool map[N][N] ; int dfn[N] , low[N] , used[N] , vist[N] , mark[N] ; int n , m , ans , id ; stack<int>p; int a[N] ; //用二分图染色的方法判断奇环 bool IsOk( int x , int w ) { vist[x] = w ; for ( int i = 1 ; i <= n ; i++ ) if ( map[x][i] ) { if ( used[i] ) { if ( vist[x] == vist[i] )//若有两个相邻的点同色,则存在奇环,返回true return true ; if ( vist[i] == -1 ) IsOk( i , w ^ 1 ) ; } } return false ; } void jud ( int s ) { int i ; memset( used , 0 , sizeof ( used )) ; //标记该分支中的点 for ( i = 0 ; i < s ; i++ ) { used[a[i]] = 1 ; } //判断是否有奇环 for ( i = 0 ; i < s ; i++ ) { memset( vist , -1 , sizeof ( vist )) ; if ( IsOk( a[i] , 1 )) break ; } //如果有奇环则将该分支中的点加起来 if ( i < s ) { for ( i = 0 ; i < s ; i++ ) if ( !mark[a[i]] ) { ans++ ; mark[a[i]] = 1 ; } } } void dfs( int x ) { dfn[x] = low[x] = ++id ; p.push( x ) ; for( int i = 1 ; i <= n ; i++ ) { if ( map[x][i] ) { if ( !dfn[i] ) { dfs( i ) ; low[x] = min ( low[x] , low[i] ) ; if ( low[i] >= dfn[x] )//点x是割点, { //将该分支中的点出栈,x留在栈中。 a[0] = x ; int k = 1 , u ; do { u = p.top(); p.pop(); a[k++] = u ; }while( u != i ) ; //判断该分支中是否有奇环 jud( k ) ; } } else if ( low[x] > dfn[i] ) low[x] = dfn[i] ; } } } int main() { int i , x , y ; while ( scanf ( "%d%d" , &n , &m ) , n + m ) { //建图 memset( map , true , sizeof( map )) ; for ( i = 1 ; i <= m ; i++ ) { scanf ( "%d%d" , &x , &y ) ; map[x][y] = map[y][x] = false ; } for( i = 1 ; i <= n ; i++ ) { map[i][i] = false ; dfn[i] = low[i] = mark[i] = 0 ; } //求强连通分支 ans = id = 0 ; for ( i = 1 ; i <= n ; i++ ) if( !dfn[i] ) dfs( i ) ; printf ( "%d\n" , n - ans ) ; } return 0 ; }