[poj] 2942 Knights of the Round Table

原题

这不是一道点双联通分量板子题。
首先,为什么这不是一道边双联通分量呢?
可能我们会认为,一个边双联通分量里的两个点之间一定存在两天不重复的路径,但事实上,这两条不重复的路径,只保证了边是不重复的,却没有保证点是不重复的。而在这道题里,显然,一个人不能坐在同一桌的两个位置。
那么为什么这是点双联通分量呢?
在一个点双联通分量里,有着像边双联通分量类似的性质,任意两点间一定存在除了起点和终点外所经过的点集互不相交的至少两条路径。
然而这就是这道题要求的!
求出点双联通分量后,我们还要判断这个点双联通分量是不是能构成奇环,而有奇环的图不是二分图,所以我们只要判断这个该点双联通分量是不是二分图就好了,所以对它进行黑白染色看是否有冲突。
另外,在求点双联通分量时要注意,要从栈里取出的是到当前v的点而不是到当前u,因为有可能我在搜u的边的时候先搜索了其他的点,而那些点没有构成一个点双联通分量,所以那样取出的点集就是错误的了。至于当前割点,我们只要手动把他加至这个点集即可。
这样这道题就算结束了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1010
#define M 500010
using namespace std;
int n,m,head[N],cnt=1,t,dfn[N],low[N],sum,num,color[N],ans,vc[N],vis[N],stk[N],r;
bool e[N][N],yes[N];
struct hhh
{
    int to,next;
}edge[2*M];

int read()
{
    int ans=0,fu=1;
    char j=getchar();
    for (;(j<'0' || j>'9') && j!='-';j=getchar()) ;
    if (j=='-') j=getchar(),fu=-1;
    for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
    return ans*fu;
}

void add(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

bool dfs(int x,int t)
{
    color[x]=t;
    for (int i=head[x],v;i;i=edge[i].next)
    {
	v=edge[i].to;
	if (vc[v]!=sum) continue;
	if (vis[v]==sum && color[v]==t) return 0;
	if (vis[v]!=sum)
	{
	    vis[v]=sum;
	    if (!dfs(v,t^1)) return 0;
	}
    }
    return 1;
}

void Tarjan(int x,int f)
{
    dfn[x]=low[x]=++t;
    stk[r++]=x;
    for (int i=head[x],v;i;i=edge[i].next)
    {
	v=edge[i].to;
	if (v==f) continue;
	if (!dfn[v])
	{
	    Tarjan(v,x);
	    low[x]=min(low[v],low[x]);
	    if (low[v]>=dfn[x])
	    {
		sum++;
		num=1;
		int tt;
		do
		{
		    tt=stk[--r];
		    vc[tt]=sum;
		    num++;
		}while(tt!=v);
		vc[x]=sum;
		if (!dfs(x,1))
		{
		    for (int i=r;i<r+num-1;i++) yes[stk[i]]=1;
		    yes[x]=1;
		}
	    }
	}
	else low[x]=min(dfn[v],low[x]);
    }
}

int main()
{
    while (~scanf("%d%d",&n,&m))
    {
	if (n==0 && m==0) break;
	cnt=1;
	r=0;
	t=sum=0;
	ans=0;
	memset(head,0,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(e,0,sizeof(e));
	memset(vis,0,sizeof(vis));
	memset(yes,0,sizeof(yes));
	memset(vc,0,sizeof(vc));
	for (int i=1,a,b;i<=m;i++)
	{
	    a=read();
	    b=read();
	    e[a][b]=1;
	    e[b][a]=1;
	}
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=n;j++)
		if (i!=j && e[i][j]!=1) add(i,j);
	for (int i=1;i<=n;i++)
	    if (!dfn[i]) Tarjan(i,0);
	for (int i=1;i<=n;i++) if (!yes[i]) ans++;
	printf("%d
",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/mrha/p/7852552.html