Knights of the Round Table--POJ 2942
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;
}