题意:在亚瑟王的圆桌骑士团中,某些骑士两两之间相互憎恨,这样一来他们开会的时候边不能相邻的坐着。即肯定存在某些人不能参加会议。假如一个骑士所有的会议都不能出席,那么他就会被驱逐。现在已知那些骑士之间相互憎恨,求最少要驱逐多少名骑士。(开会时人数必需>=3且为奇数)
题解:建图时,对互相不憎恨的两人之间连一条边。对任意一名骑士来说,他要能出席某次会议则他左右的人都不能与他互相憎恨。将每次参加会议的所有人(不一定是整个骑士团,只需人数>=3且为奇数)看做一个点双联通分量,那么每个点都至少有两个点与他相邻。即需要保证双联通分量中存在奇圈。因为只要点双连通分量中存在奇圈,那么这个分量中所有的点都可以出现在奇圈上。求奇圈时可以用到这样一个性质,一个图是二分图当且仅当图中不存在奇圈。那么我们只需判断一个图是否是二分图就可以判断此图存在奇圈,可以用交替染色。
#include <iostream>
using namespace std;
#define N 1005
#define min(a,b) (a<b?a:b)
int n, m;
int size, id, scc;
int color[N], head[N];
int low[N], dfn[N], block[N];
int stack[N], temp[N], top, cnt; // temp 是一个临时的栈,存储点双连通分支
bool map[N][N], expell[N], instack[N]; // expell[i]==true表示i需要被驱逐
struct { int v, next; } edge[N*1000];
bool odd_cycle ( int u, int clr ) //判断是否存在奇圈
{
color[u] = clr;
for ( int i = head[u]; i; i = edge[i].next )
{
int v = edge[i].v;
if ( block[v] == scc )
{
if ( color[v] && color[v] == color[u] )
return true;
if ( !color[v] && odd_cycle (v, -clr) )
return true;
}
}
return false;
}
void Tarjan ( int u, int father )
{
int v, t;
stack[++top] = u;
instack[u] = true;
dfn[u] = low[u] = ++id;
for ( int i = head[u]; i; i = edge[i].next )
{
v = edge[i].v;
if ( v == father ) continue;
if ( ! dfn[v] )
{
Tarjan ( v, u );
low[u] = min ( low[u], low[v] );
if ( low[v] >= dfn[u] ) // u是割点
{
scc++;
do {
t = stack[top--];
instack[t] = false;
block[t] = scc;
temp[++cnt] = t; //出栈的同时把所有的点记录在temp中,即用temp来储存双连通分支内所有的点
} while ( t != v ); //注意不要把u出栈,因为一个割点可能属于不同的双联通分支
temp[++cnt] = u; // 割点u属于不同的双联通分支,所以它必然也属于temp
memset(color,0,sizeof(color)); // 所有颜色置为0
if ( cnt >= 3 && odd_cycle(u,1) ) // 当temp中存在奇圈,那么temp中的所有人都可以留下
{
while ( cnt != 0 )
expell[temp[cnt--]] = false;
}
else cnt = 0;
}
}
else if ( instack[v] )
low[u] = min ( low[u], dfn[v] );
}
}
void initial()
{
top = cnt = 0;
size = id = scc = 0;
memset(map,0,sizeof(map));
for ( int i = 1; i <= n; i++ )
{
expell[i] = true;
instack[i] = false;
dfn[i] = low[i] = block[i] = head[i] = 0;
}
}
void add ( int u, int v )
{
size++;
edge[size].v = v;
edge[size].next = head[u];
head[u] = size;
}
int main()
{
int u, v, i, j;
while ( scanf("%d%d",&n,&m) && (m+n) )
{
initial();
while ( m-- )
{
scanf("%d%d",&u,&v);
map[u][v] = map[v][u] = true;
}
for ( i = 1; i <= n; i++ )
for ( j = i+1; j <= n; j++ )
if ( ! map[i][j] )
add(i,j), add(j,i);
for ( i = 1; i <= n; i++ )
if ( ! dfn[i] ) Tarjan(i,-1);
int res = 0;
for ( i = 1; i <= n; i++ )
if ( expell[i] ) res++;
printf("%d\n", res );
}
return 0;
}