【POJ2942】Knights of the Round Table

题目

题目链接: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;
}
posted @ 2020-06-10 20:45  stoorz  阅读(116)  评论(0编辑  收藏  举报