1、题目类型:图论、点双强连通分量、Tarjan算法。
2、解题思路:题意,N个骑士中某些骑士之间会有仇恨。骑士们开会时围坐在一个圆桌旁。一次会议能够举行,当且仅当没有相邻的两个骑士相互仇恨,且开会人数为大于2的奇数。若某个骑士任何会议都不能参加,那么就必须将它踢出。给出骑士之间的仇恨关系,问需要踢出多少个骑士。步骤,(1)建立输入图的补图;(2)类似Trajan算法求解并记录点双强连通分量;(3)寻找最大强连通分量奇圈。
3、注意事项:两条重要定理:若某块不可染色为二分图,则该块存在奇圈;若某块存在奇圈,那么该块中的所有点都存在与奇圈中;表示图的节点间关系时,用vector表示,用邻接表表示TLE。
4、实现方法:
#define MAXN 1010
#include < iostream >
#include < vector >
using namespace std;
int color[MAXN],use[MAXN],mat[MAXN][MAXN];
vector < int > map[MAXN];
int n,m,cnt,ans;
int b[MAXN];
bool IsOk( int v, int val)
{
int i,j;
color[v] = val;
for (j = 0 ;j < map[v].size();j ++ )
{
i = map[v][j];
if (i == v)
continue ;
if (b[i])
{
if (color[v] == color[i])
return true ;
if (color[i] ==- 1 )
IsOk(i,val ^ 1 );
}
}
return false ;
}
void Dummy( int t, int * a)
{
int i,j;
memset(b, 0 , sizeof (b));
for (j = 0 ;j < t;j ++ )
b[a[j]] = 1 ;
for (i = 0 ;i < t;i ++ )
{
memset(color, - 1 , sizeof (color));
if (IsOk(a[i], 1 ))
break ;
}
if (i < t)
{
for (j = 0 ;j < t;j ++ )
{
if ( ! use[a[j]])
{
ans ++ ;
use[a[j]] = 1 ;
}
}
}
}
void Search( int n,vector < int > mat[MAXN], int * dfn, int * low, int now, int & cnt, int * st, int & sp)
{
int i,j,m,a[MAXN];
dfn[st[sp ++ ] = now] = low[now] =++ cnt;
for (j = 0 ;j < mat[now].size();j ++ )
{
i = mat[now][j];
if ( ! dfn[i])
{
Search(n,mat,dfn,low,i,cnt,st,sp);
if (low[i] < low[now])
low[now] = low[i];
if (low[i] >= dfn[now])
{
for (st[sp] =- 1 ,a[ 0 ] = now,m = 1 ;st[sp] != i;a[m ++ ] = st[ -- sp]);
Dummy(m,a);
}
}
else if (dfn[i] < low[now])
low[now] = dfn[i];
}
}
// 求点双连通分量即块
void Block( int n,vector < int > mat[MAXN])
{
int i,cnt,dfn[MAXN],low[MAXN],st[MAXN],sp = 0 ;
for (i = 0 ;i < n;dfn[i ++ ] = 0 );
for (cnt = i = 0 ;i < n;i ++ )
if ( ! dfn[i])
Search(n,mat,dfn,low,i,cnt,st,sp);
}
void Init()
{
int i,j,a,b;
memset(mat, 0 , sizeof (mat));
for (i = 0 ;i < m;i ++ )
{
scanf( " %d%d " , & a, & b);
a -- ;
b -- ;
mat[a][b] = mat[b][a] = 1 ;
}
// 转换为补图
for (i = 0 ;i < n;i ++ )
map[i].clear();
for (i = 0 ;i < n;i ++ )
{
for (j = 0 ;j < n;j ++ )
{
if ( ! mat[i][j])
{
map[i].push_back(j);
map[j].push_back(i);
}
}
}
memset(use, 0 , sizeof (use));
cnt = 0 ,ans = 0 ;
}
int main()
{
while (scanf( " %d%d " , & n, & m))
{
if (n == 0 && m == 0 )
break ;
Init();
Block(n,map);
cout << n - ans << endl;
}
return 0 ;
}