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 ;
}

 

 

posted @ 2012-11-13 17:29  Misty_1  阅读(815)  评论(0编辑  收藏  举报