poj 2942 求块+查找奇圈

题意描述: 有n个骑士,现在要求骑士坐在圆桌上,相邻的两个位置不能坐有冲突的骑士,并且每个圆桌要求坐奇数个人,现在要求出有不能坐在圆桌上的人数(这里要求能坐的尽量坐)


分析:因为题目给出的是有冲突的两个人,那么我们根据题目给出的图建立一个图,两个可以坐在相邻位置的人连边,那么假设他们形成了一个简单的无向环,则很明显如果这个环上的顶点个数为奇数,那么这个环上的人可以坐在一个圆桌上,若顶点数为偶数的话,那么也很显然这些人都不能坐参加会议!如果他们形成了一个复杂的环(即环中有环),若过最大的环为奇数环,我们就直接可以安排他们按最大环上的做,若果最大环为偶数环,如果我能将其拆成若干个奇数环,那么我们就可以安排他们做奇数环个圆桌,否则他们都不能坐,其实说的那么复杂,其实就是先求出图中的双连通分量(顶点个数>=2),若双连通分量中存在奇环,那么这些人都可以坐上圆桌,否则环中的人都不能坐上圆桌,这里因为割点可能会属于多个双连通子图,那么对于割点我们需得特殊处理!!现在就是判断一个环是否存在奇环,那我通过二分染色的方法就可以解决,若染色后,出现两个相同的顶点出现相同的颜色,那么则存在奇环,否则不存在奇环!!


总结:这个人啊,一旦养成烂习惯就很难改掉,我遇到难点的题目就只是简单的想了一下,根本没有多深入的思考,想不出来就去搜别人的解题报告,这是个破习惯,得改啊,所以我在此我做个决定,下次一定绞尽脑汁去思考,我相信只有多思考才能进步得更快!!(能看懂和自己能够解决之间的差距是很大的)!!


代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N = 1050;
int g[N][N],n;
int dfn[N],low[N],st[N],index,top,vis[N];
int flag[N],block[N];
bool Vis[N];
int Min(int a,int b)
{
	return a>b?b:a;
}

int bfs(int u)
{
	int que[N],head=0,tail=1;
	que[0]=u;
	flag[u]=1;
    while(head!=tail)
	{
		u=que[head];
		head=(head+1)%N;
     	for(int v=1;v<=n;v++)
		  if(g[u][v]&&vis[v])
		     if(flag[v]==-1)
			 {
				 flag[v]=(flag[u]+1)%2;
				 que[tail]=v;
				 tail=(tail+1)%N;
			 }
		    else if(flag[v] == flag[u])
			   return 0;
	}
	return 1;
}

void Cutvedex(int u)
{
	int i;
	dfn[u]=low[u]=index++;
	st[++top]=u;
	for(int v=1;v<=n;v++)
	    if(g[u][v])
		{
			if(dfn[v]==-1)
			{
		    	Cutvedex(v);
				low[u]=Min(low[u],low[v]);
			    if(low[v]>=dfn[u])
				{
					memset(vis,0,sizeof(vis));
					block[0]=0;
					while(true)
					{
						vis[st[top]]=1;
						block[++block[0]]=st[top];
						if(st[top--]==v)break;
					}
					vis[u]=1;
					block[++block[0]]=u;
					for( i=1;i<=n;i++)flag[i]=-1;
					if(!bfs(u))//表示若为一个奇环
				     for(i=1;i<=block[0];i++)
						 Vis[block[i]]=1;  //标记能坐在圆桌上的人   
				}
			}
			else low[u]=Min(low[u],dfn[v]);
		}
}

int main ()
{
    int m,i,j,x,y;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(n==0&&m==0)break;
		for(i=1;i<=n;i++)     //初始化化原图
			for(j=1;j<=n;j++)
			    if(i!=j)
					g[i][j]=1;
		for(i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			g[x][y]=0;
			g[y][x]=0;
		} 
		memset(dfn,-1,sizeof(dfn));
	   	memset(low,0,sizeof(low));	
		memset(Vis,0,sizeof(Vis));		
		top=0;index=1;
		for(i=1;i<=n;i++)
			if(dfn[i]==-1)
	        	Cutvedex(i);
	    int res=0;
		for(i=1;i<=n;i++)
			if(Vis[i])
				res++;
		printf("%d\n",n-res);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值