题目
题目链接:http://poj.org/problem?id=2942
有 \(n\) 个骑士经常举行圆桌会议,商讨大事。每次圆桌会议至少有 3 个骑士参加,且相互憎恨的骑士不能坐在圆桌的相邻位置。如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是大于 1 的奇数,以防止赞同和反对票一样多。知道那些骑士相互憎恨之后,你的任务是统计有多少骑士不可能参加任何一个会议。
思路:
首先如果骑士 \(i\) 和 \(j\) 不互相憎恨,那么就将 \(i,j\) 连边。此时一个会议选择的骑士应当是一个奇环内的所有点。
那么对于任意两个骑士 \(x,y\),如果 \(x\) 和 \(y\) 不在一个点双连通分量内,那么 \(x\) 和 \(y\) 不可能同时出席一个会议。因为 \(x\) 和 \(y\) 不可能处于同一个环中。
Tarjan 求出每一个点双连通分量,容易发现,如果这个点双连通分量中如果有奇环,那么整个点双连通分量的点都会被至少一个奇环包含。
那么只需判断每个连通分量是否包含奇环即可。如果一个点双连通分量不含奇环,那么它必然是一个二分图。果断二分图染色。
时间复杂度 \(O(n^2)\)。
代码
#include <stack>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1010;
int n,m,ans,cnt,tot,col[N],head[N],dfn[N],low[N];
bool hate[N][N],flag[N],vis[N];
stack<int> st;
vector<int> dcc[N];
struct edge
{
int next,to;
}e[N*N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
bool dfs(int x)
{
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (flag[v])
{
if (col[v]==col[x]) return 0;
if (!col[v])
{
col[v]=col[x]^1;
if (!dfs(v)) return 0;
}
}
}
return 1;
}
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
st.push(x);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);
if (low[v]>=dfn[x])
{
int y=0;
cnt++;
do {
y=st.top();
st.pop();
dcc[cnt].push_back(y);
} while (y!=v);
dcc[cnt].push_back(x);
}
}
else low[x]=min(low[x],dfn[v]);
}
}
int main()
{
while (scanf("%d%d",&n,&m)>0 && n)
{
memset(vis,0,sizeof(vis));
memset(dcc,0,sizeof(dcc));
memset(dfn,0,sizeof(dfn));
memset(hate,0,sizeof(hate));
memset(head,-1,sizeof(head));
tot=ans=cnt=0;
for (int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
hate[x][y]=hate[y][x]=1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (!hate[i][j] && i!=j)
add(i,j),add(j,i);
tot=0;
for (int i=1;i<=n;i++)
if (!vis[i])
{
while (st.size()) st.pop();
tarjan(i);
}
for (int i=1,k;i<=cnt;i++)
{
for (int j=0;j<dcc[i].size();j++)
flag[dcc[i][j]]=1;
col[dcc[i][0]]=114514;
k=!dfs(dcc[i][0]);
for (int j=0;j<dcc[i].size();j++)
{
vis[dcc[i][j]]|=k;
flag[dcc[i][j]]=col[dcc[i][j]]=0;
}
}
for (int i=1;i<=n;i++)
ans+=vis[i];
printf("%d\n",n-ans);
}
return 0;
}