poj Knights of the Round Table 点双连通分量

题意:

有n个骑士,要去参加人数为奇数且大于1的圆桌会议,有的骑士不能坐在一起,现在要处死一些骑士,使得剩下的骑士都有圆桌会议可以参加,注意不是要剩下的骑士一起去开会,只要有能参加会议的可能就好。

分析:

求补图后每条边连的是可以参加会议的骑士。对补图求点双连通分量,求的的每个点双连通分量中都没有割点,此时若这个点双连通分量中含有奇圈Q,则其中任何一点v都可以找到一个奇圈,因为如果v在Q上,那Q为所求,如果v不在Q上,那么根据点强连通分量的性质,v肯定可以走出两条没有公共点的路连到Q上,把Q分为一奇一偶两部分,总可以构造出一个包含v的奇圈。

代码:

//poj 2942
//sepNINE
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int maxN=1024;
vector<int> edge[maxN];
vector<vector<int> > connect;
stack<int> s;
int n,m,t,cnt;
int g[maxN][maxN];
int dfn[maxN],low[maxN],list[maxN];
int save[maxN],color[maxN];
void biconnect(int v)
{
	s.push(v);
	dfn[v]=low[v]=++t;
	int i,succ;
	for(i=edge[v].size()-1;i>=0;--i){
		succ=edge[v][i];
		if(!dfn[succ]){
			biconnect(succ);
			if(low[succ]>=dfn[v]){
				++cnt;	
				int k,len=0;
				do{
					k=s.top();
					s.pop();
					list[len++]=k;
				}while(k!=succ);
				list[len++]=v;
				vector<int> tmp(list,list+len);
				connect.push_back(tmp);
			}
			low[v]=min(low[v],low[succ]);
		}else 
			low[v]=min(low[v],dfn[succ]);
	}
}

int meeting(int index)
{
	memset(color,-1,sizeof(color));	
	vector<int> people=connect[index];
	queue<int> Q;
	color[people[0]]=1;
	Q.push(people[0]);
	while(!Q.empty()){
		int u=Q.front();
		Q.pop();
		for(int j=0;j<people.size();++j){
			int v=people[j];
			if(u!=v&&g[u][v]==0){
				if(color[v]==-1){
					color[v]=1-color[u];
					Q.push(v);	
				}
				else if(color[v]==color[u])
					return 1;
			}
		}
	}
	return 0;
}
int main()
{
	int i,j,a,b;
	while(scanf("%d%d",&n,&m)==2&&n){
		memset(g,0,sizeof(g));
		memset(dfn,0,sizeof(dfn));
		for(i=1;i<=n;++i) edge[i].clear();
		connect.clear();
		while(!s.empty()) s.pop();
		while(m--){
			scanf("%d%d",&a,&b);	
			g[a][b]=g[b][a]=1;
		}	
			
		for(i=1;i<=n;++i)
			for(j=1;j<=n;++j)
				if(j!=i&&g[i][j]==0)
					edge[i].push_back(j);			

		t=cnt=0;
		for(i=1;i<=n;++i)
			if(!dfn[i])
				biconnect(i);
		memset(save,0,sizeof(save));
		for(i=connect.size()-1;i>=0;--i)
			if(connect[i].size()>2&&meeting(i))
				for(j=connect[i].size()-1;j>=0;--j)
					save[connect[i][j]]=1;
		int ans=0;
		for(i=1;i<=n;++i) if(save[i]==0) ++ans;
		printf("%d\n",ans);	
	}	
	return 0;	
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值