LA 3523 Knights of the Round Table(点双连通分量+二分图判断)

185 篇文章 0 订阅
50 篇文章 0 订阅

题意:有n个骑士经常举行圆桌会议,商讨大事,每次圆桌会议至少应有3个骑士参加,且互相憎恨的骑士不能坐在圆桌旁的相邻位置,如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是奇数,以防赞同和反对票一样多,知道哪些骑士互相憎恨之后,你的任务是统计有多少个骑士不可能参加任何一个会议。


分析:求出原图的补图后,问题转化为判断一个点是否存在一个奇数环内,一个环内的所有点一定在同一个点双连通分量内,我们求出所有的点双连通分量后,判断一下它是否为一个二分图,若不是二分图则连通分量内的所有点均至少存在一个奇环内。注意对割点染色时的特殊判断。


#include <cstdio>
#include <stack>
#include <queue>    
#include <vector>    
#include <cstdio>    
#include <utility>    
#include <cstring>    
#include <iostream>    
#include <algorithm>    
#define INF 0x3f3f3f3f
#define MOD 1000000007
using namespace std;
int n,m,ans,bcc_cnt,dfs_clock,pre[1005],bccno[1005],low[1005],color[1005];
bool hate[1005][1005],isans[1005];
struct Edge
{
	int u,v;
	Edge(int x,int y)
	{
		u = x;
		v = y;
	}
}; 
stack <Edge> S;
vector <int> bcc[1005];
int dfs(int u,int fa)
{
	low[u] = pre[u] = ++dfs_clock;
	for(int v = 1;v <= n;v++)
	 if(!hate[u][v])
	 {
	 	Edge e = Edge(u,v);
		if(!pre[v])
	 	 {
	  		S.push(e);
	  		low[u] = min(low[u],dfs(v,u));
	  		if(low[v] >= pre[u])
	  		{
				bcc[++bcc_cnt].clear();	
				for(;;)
				{
					Edge x = S.top();
					S.pop();
					if(bccno[x.u] != bcc_cnt) 
					{
						bcc[bcc_cnt].push_back(x.u);
						bccno[x.u] = bcc_cnt;
					}
					if(bccno[x.v] != bcc_cnt)
					{
						bcc[bcc_cnt].push_back(x.v);
						bccno[x.v] = bcc_cnt;
					}
					if(x.u == u && x.v == v) break;
				}
			}
	  	}
	  	else 
		    if(pre[v] < pre[u] && v != fa)
	  		{
				S.push(e);
				low[u] = min(low[u],pre[v]);
	 		}
	 }
	 return low[u];
}
bool jud(int u,int colm,int edg)
{
	bool flag = true;
	color[u] = colm;
	for(int v = 1;v <= n;v++)
	 if(!hate[u][v] && bccno[v] == edg)
	  if(!color[v]) flag = flag && jud(v,3-colm,edg);
	  else 
	   if(color[v] != 3-colm) return false;
	return flag;
}
int main()
{
	while(scanf("%d%d",&n,&m) == 2)
	{
		if(n == 0 && m == 0) break;
		memset(hate,0,sizeof(hate));
		memset(pre,0,sizeof(pre));
		memset(bccno,0,sizeof(bccno));
		memset(isans,0,sizeof(isans));
		bcc_cnt = ans = 0;
		for(int i = 1;i <= n;i++) hate[i][i] = true;
		for(int i = 1;i <= m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			hate[x][y] = hate[y][x] = true;
		}
		for(int i = 1;i <= n;i++)
		{
			dfs_clock = 0;
			if(!pre[i]) dfs(i,-1);
		}
		for(int i = 1;i <= bcc_cnt;i++)
		{
			memset(color,0,sizeof(color));
			if(bcc[i].size() <= 2) continue;
			for(int u : bcc[i]) bccno[u] = i;
			bool flag = !jud(bcc[i][0],1,i);
			for(int u : bcc[i]) isans[u] = isans[u] || flag;
		}
		for(int i = 1;i <= n;i++) 
		 if(isans[i]) ans++;
		printf("%d\n",n-ans); 
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值