poj 2942 圆桌骑士 无向图割点 奇圈 交叉染色

连通类经典题

题意及分析参考:

1.建反向图 2.tarjan 算法求割点3.二部图与奇圈 4.交叉染色

http://blog.csdn.net/lyy289065406/article/details/6756821

算法:

无向图寻找割点的 tarjan 算法,叉分染色。

目标:

找出图中所有的奇圈(点数大于 3),并标记在圈上的点,最后计算出未被标记的点数。

PS:

记得找到奇圈才是关键: low[v] >= dfn[u] ( tarjan 算法 )

做完这道题,很累,真的自己想不出来,后来发现,自己把 tarjan 算法的几个关键都托付给模板了。。。(有向图、无向图的割点、桥)

// zstu_wangrui	2942	Accepted	4236K	1094MS	C++	2397B	2013-08-02 19:02:41
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int min( int a, int b ){ return a < b ? a : b; }
int max( int a, int b ){ return a > b ? a : b; }
struct Edge{
	int v, sign, next;
}edge[maxn*maxn];
int tot, head[maxn];
struct EE{
	int u, v;
	EE(){}
	EE( int a, int b ) : u(a), v(b){}
}st[maxn];
int top;
int n, r, E[maxn][maxn];
void init()
{
	memset(E, -1, sizeof(E));
	tot = 0;
	memset(head, -1, sizeof(head));
}
void add_edge( int u, int v )
{	
	edge[tot].v = v;
	edge[tot].sign = 0;
	edge[tot].next = head[u];
	head[u] = tot++;
	edge[tot].v = u;
	edge[tot].sign = 0;
	edge[tot].next = head[v];
	head[v] = tot++;
}
int color[maxn], ans, flag, In[maxn], vis[maxn];
void dfs( int u, int p, int col )
{
	color[u] = col;
	int i, v;
	for(i = head[u]; i != -1; i = edge[i].next)
	{	
		v = edge[i].v;
		if(vis[v])
		{
			if(color[v] == -1)
				dfs( v, u, !col );
			else if(color[v] == color[u])
				flag = 1;
			if(flag)return ;
		}
	}
}
int tmpdfn, dfn[maxn], low[maxn];
void tarjan( int u )
{	
	low[u] = dfn[u] = tmpdfn++;
	int i, v, j;
	EE t;
	for(i = head[u]; i != -1; i = edge[i].next)if(!edge[i].sign)
	{	
		v = edge[i].v;
		edge[i].sign = edge[i^1].sign = 1;
		if(dfn[v] == -1)
		{
			EE temp( u, v );
			st[top++] = temp;
			tarjan( v );
			low[u] = min( low[u], low[v] );
			if(low[v] >= dfn[u])    //割点
			{
				memset(vis, 0, sizeof(vis));
				do{ 
					t = st[--top];
					vis[t.u] = vis[t.v] = 1;
				}while( top >= 0 && !(t.u == temp.u && t.v == temp.v) );
				flag = 0;
				memset(color, -1, sizeof(color));
				dfs( u, u, 1 );
				if(flag)for(j = 1; j <= n; j++)if(vis[j])
					In[j] = 1;
			}
			if( low[v] > dfn[u] );//桥
		}
		else low[u] = min( low[u], dfn[v] );
	}
	if( low[u] == dfn[u] );//缩点时刻 

}
void SCC()
{
	int i;
	top = 0;
	tmpdfn = 1;
	memset(dfn, -1, sizeof(dfn));
	for(i = 1; i <= n; i++)if(dfn[i] == -1)tarjan( i );
}

int main()
{
	int u, v, i, j;
	while( ~scanf( "%d%d", &n, &r ), n + r )
	{
		init();
		while( r-- )
		{
			scanf( "%d%d", &u, &v );
			E[u][v] = E[v][u] = 0;
		}
		for(u = 1; u <= n; u++)for(v = u + 1; v <= n; v++)if(E[u][v])
			add_edge( u, v );	
		memset(In, 0, sizeof(In));
		SCC();
		ans = 0;
		for(i = 1; i <= n; i++)if(In[i])
			ans++;
		printf( "%d\n", n - ans );
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值