Knights of the Round Table (补图+点连通分量缩点板子+二分图判定奇环)

14 篇文章 0 订阅
7 篇文章 0 订阅
该博客主要探讨了一道图论问题,涉及图的邻接矩阵、补图、强连通分量、二分图染色等概念。博主通过tarjan算法寻找图的连通分量,并在补图上判断是否存在奇环。对于每个连通分量,使用染色法检查是否存在奇数环,从而找出那些不位于奇环中的骑士。博客以POJ上的一个问题为背景,介绍了算法实现过程及调试经验,并提供了一组测试数据。
摘要由CSDN通过智能技术生成

http://poj.org/problem?id=2942


思路:首先考虑建边,但是会发现直接建边很难处理。

那么考虑通过能相邻的图去建立边.建完边后结合题意,题目说要求一些骑士永远不可能去开会。开会的要求有一个>=3的奇环。

所以题目问的是哪些骑士是不在任何一个奇环里的。

所以考虑把补图上所有的缩点后的点联通分量里面判一下奇环。

奇环的判定也就是染色法判定是不是二分图。

这个题大概调了5小时。

做了之后发现点连通分量的板子是不一样的,因为割点是属于多个连通分量里面的,不能直接按照求强连通分量的缩点那样直接弹出栈。

另外注意在主函数里面把每个dcc[]打上对应的编号,然后在判奇数环的时候注意在补图上判奇数环,dcc[]里面是没有连边关系的。同时注意遍历点的时候是要在同一个dcc[]里的

提供一组数据:

7 12
1 7
2 1
3 6
7 6
1 4
4 7
4 2
1 5
2 3
2 7
6 5
5 3
0 0
ans:7

http://poj.org/bbs?problem_id=2942(更多的见discuss)

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e3+100;
typedef int LL;
bool ma[maxn][maxn];///表示不能相邻的图
stack<LL>s;
vector<LL>g[maxn];//补图
vector<LL>dcc[maxn];
LL dfn[maxn],low[maxn],times=0,cnt=0,fa[maxn],id[maxn];
bool ge[maxn],oddcircle[maxn];///奇环上的标记
LL col[maxn];//二分图染色 1--黑;2---白
void init1(){
	times=0;cnt=0;
	for(LL i=0;i<maxn;i++) g[i].clear(),dcc[i].clear();
	memset(ma,0,sizeof(ma));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(fa,-1,sizeof(fa));
	memset(ge,0,sizeof(ge));
	memset(oddcircle,0,sizeof(oddcircle));
	memset(id,0,sizeof(id));
}
bool flag=1;
void init2()
{
    flag=1;
	memset(col,0,sizeof(col));
}
void tarjan2(LL x)
{
	dfn[x]=low[x]=++times;
	s.push(x);
	LL child=0;
	if(fa[x]==-1&&g[x].size()==0){
		dcc[++cnt].push_back(x);
		return;
	}
	for(LL i=0;i<g[x].size();i++){
		LL to=g[x][i];
		if(!dfn[to])
		{
			fa[to]=x;child++;
			tarjan2(to);
			low[x]=min(low[x],low[to]);
			if(low[to]>=dfn[x])
			{
				child++;
				if(fa[x]!=-1||child>1) ge[x]=true;
				cnt++;
				LL z;
				do{
					z=s.top();s.pop();
					dcc[cnt].push_back(z);
				}while(z!=to);
				dcc[cnt].push_back(x);
			}
		}
		else if(to!=fa[x]) low[x]=min(low[x],dfn[to]);
	}

}

void  dfs(LL u,LL color)//注意二分图要找一整个dcc图中是不是存在奇数度的环
{
  col[u]=color;
  ///cout<<"col["<<u<<"]="<<color<<endl;
  for(LL i=0;i<g[u].size();i++){
  	LL v=g[u][i];
  	if(id[u]!=id[v]) continue;//不在染色的dcc上
	if(col[v]==col[u]){
  		flag=0;break;
	}
	else if(!col[v]){
		dfs(v,3-color);
	}
  }
}
bool check(LL x)
{
	init2();
	dfs(x,1);
///	LL q=id[x];

 //   cout<<"id:"<<endl;
///	for(LL i=0;i<dcc[q].size();i++){
 ///       cout<<id[dcc[q][i]]<<" ";
///	}
///	cout<<endl;



	///cout<<"染色"<<endl;
	///for(LL i=0;i<dcc[q].size();i++){
  ///      cout<<col[dcc[q][i]]<<" ";
	///}
	///cout<<endl;
	if(  flag==0) {


		///cout<<"是奇环"<<endl;
		return true;
	}
	else {
	 ///   cout<<"不是奇环"<<endl;
		return false;
	}
}
int main(void)
{
  LL n,m;
  while(scanf("%d%d",&n,&m)&&(n+m))
  {
	init1();
  	for(LL i=1;i<=m;i++){
  		LL x,y;scanf("%d%d",&x,&y);
  		ma[x][y]=1;ma[y][x]=1;
  	}
  	for(LL i=1;i<=n;i++){
  		for(LL j=i+1;j<=n;j++)
  		{
  			if(!ma[i][j]) g[i].push_back(j),g[j].push_back(i);
		}
	}
  	for(LL i=1;i<=n;i++){
  		if(!dfn[i]) tarjan2(i);
  	}

///  	cout<<"dcc的数量:"<<cnt<<endl;
 /// 	for(LL i=1;i<=cnt;i++){
 /// 		cout<<"dcc["<<i<<"]="<<dcc[i].size()<<": ";
///		for(LL j=0;j<dcc[i].size();j++)
	///	{
	//		cout<<dcc[i][j]<<" ";
	//	}
	///	cout<<endl;
	}

    for(LL i=1;i<=cnt;i++){
    	for(LL j=0;j<dcc[i].size();j++) id[dcc[i][j]]=i;

    	if(dcc[i].size()>=3&&check(dcc[i][0])==true){
    		for(LL j=0;j<dcc[i].size();j++)
    		{
    	///		cout<<"是奇环"<<endl;
    			oddcircle[dcc[i][j]]=true;
			}
		}
	}
	LL sum=0;
	for(LL i=1;i<=n;i++){
		if(oddcircle[i]==1) sum++;
	}
	printf("%d\n",n-sum);
  }
return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值