POJ2942:点连通分量+二分图染色

POJ2942

题解:

  • 首先把不敌对的骑士连一条边,即原图的补图。
  • 要开一个会议,得形成一个环。所以求点-双连通分量。
  • 考虑每个连通分量,人数必须为奇数个。所以进行二分图奇偶染色。
  • 这里有一个结论:二分图一定是偶圈。如果染色失败,那么说明是奇圈,满足条件。
  • 还有一个结论:如果存在一个环为奇圈,那么整个连通分量的每一点都在某个奇圈当中。

代码:

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int const N = 1000 + 10;
int n,m,mp[N][N];
int cnt,bcc_cnt;
int dfn[N],lowlink[N],bccno[N];
int color[N],odd[N];
vector<int>G[N],bcc[N];
struct Edge
{
	int u,v;
	Edge(int uu,int vv):u(uu),v(vv){};
};
stack<Edge>st;
void Init(){
	for(int i=1;i<=n;i++)	G[i].clear();
	memset(mp,0,sizeof(mp));
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		mp[u][v] = mp[v][u] = true;
	}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(!mp[i][j])	G[i].push_back(j),	G[j].push_back(i);
}
void dfs(int u,int fa){
	dfn[u] = lowlink[u] = ++cnt;
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(v == fa)	continue;
		if(!dfn[v]){
			st.push(Edge(u,v));
			dfs(v,u);
			lowlink[u] = min(lowlink[v],lowlink[u]);
			if(lowlink[v] >= dfn[u]){
				bcc_cnt++;	bcc[bcc_cnt].clear();
				while(1){
					Edge e = st.top();	st.pop();
					if(bccno[e.u] != bcc_cnt)	bcc[bcc_cnt].push_back(e.u),	bccno[e.u] = bcc_cnt;
					if(bccno[e.v] != bcc_cnt)	bcc[bcc_cnt].push_back(e.v),	bccno[e.v] = bcc_cnt;
					if(e.u == u && e.v == v)	break;
				}
			}
		}else if(dfn[v] < dfn[u]){
			st.push(Edge(u,v));
			lowlink[u] = min(lowlink[u],dfn[v]);
		}
	}
}
void Tarjan(){
	cnt = bcc_cnt = 0;
	memset(dfn,0,sizeof(dfn));
	memset(lowlink,0,sizeof(lowlink));
	memset(bccno,0,sizeof(bccno));
	for(int i=1;i<=n;i++)
		if(!dfn[i])	dfs(i,0);
}
bool dfs2(int u,int c,int bcc){
	color[u] = c;
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(bccno[v] != bcc)	continue;
		if(color[v] == c)	return false;
		if(!color[v] && !dfs2(v,-c,bcc))	return	false;
	}
	return	true;
}
int solve(){
	memset(odd,0,sizeof(odd));
	for(int i=1;i<=bcc_cnt;i++){
		memset(color,0,sizeof(color));
		for(int j=0;j<bcc[i].size();j++)	bccno[bcc[i][j]] = i;    //这里必须更新,因为一个定点可能属于多个连通分量
		int u = bcc[i][0];
		if(!dfs2(u,1,i)){   
			for(int j=0;j<bcc[i].size();j++)	odd[bcc[i][j]] = true;
		}
	}
	int sum = n;
	for(int i=1;i<=n;i++)	if(odd[i])	sum--;
	return sum;
}
int main(){	
	while(~scanf("%d%d",&n,&m)){
		if(!n && !m)	break;
		Init();
		Tarjan();
		printf("%d\n",solve());
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值