poj1703 Find them, Catch them

补记:

关于非递归版本为何会超时,今天早晨就有了答案。请教同学之后,了解到真正的原因:STL对于算法练习的代码而言,还是太慢了(说STL效率高是在实际工程中,使用STL一般不会拖后腿)。改用数组模拟栈就不超时了,耗时比递归稍微少一些,可以忽略不计(看来理论上说得通的东西实际中总体还是不会错的)。

//非递归版本的find函数。递归版本忘记怎么写了,就写一个非递归版本。
//事实上find就做了一件事:
//1.如果x==p[x],返回x的值
//2.否则,从叶子结点x出发一路向上直到找到树根,并把沿途经过的所有结点(包括x)都指向树根,即参照案件都设置为树根上面的案件,并更新关系。
int myFind(int x)
{
	if(x==p[x]) return x;

	int wp=x;	//wp means waypoints
	int ind=-1;	//栈空时候的指针位置

	while (wp!=p[wp])
	{
		wpstack[++ind]=wp;//wpstack是用于模拟栈的全局数组
		wp=p[wp];
	}	//此while结束后wp就是始祖了

	int cf=wp;	//cf means current father
	int cn;	//cn means current node
	while (ind!=-1)
	{
		cn=wpstack[ind--];
		p[cn]=wp;	//把沿途所有结点指向始祖wp
		r[cn]=(r[cn]+r[cf])%2;	//更新与始祖wp的关系
		cf=cn;	//更新当前父结点
	}

	return wp;
}

这是题目:

3:Find them, Catch them
查看 提交 统计 提问
总时间限制: 1000ms 内存限制: 65536kB
描述
The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)

Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:

1. D [a] [b]
where [a] and [b] are the numbers of two criminals, and they belong to different gangs.

2. A [a] [b]
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.
输入
The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.
输出
For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."
样例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
样例输出
Not sure yet.
In different gangs.
In the same gang.
来源
POJ Monthly--2004.07.18


========================================================================

带关系的并查集,比poj1182简单一些。忘记递归形式的find函数怎么写了,就写了一个非递归的版本,反正find函数说到底只做了一件事:将沿途所有结点都指向始祖结点并分别更新它们与始祖结点的关系。出乎意料的是,这个非递归版本居然超时了(虽然结果正确)!不明觉厉啊,谁说非递归比较快的?好吧,可能是自己非递归版本写得太渣了...anyway,我把代码都po出来了,如果足够幸运的话,会有好心人告诉我为什么的吧。

========================================================================


代码清单:

//感觉像是一道并查集的题目,带关系的并查集
#include <cstdio>
#include <stack>
using namespace std;

#define MAXN 100005	//最大案件数量

int p[MAXN];	//它的“父亲”案件是谁
int r[MAXN];	//它与“父亲”案件的关系。0同一帮派1不同帮派
//这里的“父亲”案件其实是我们选出来的参照案件
//并查集的最终目标就是,把所有案件的参照案件都变成同一个案件,即,并到同一棵树上去,即树根都是同一个案件。
//这样任意两个案件a, b的关系就能通过它们与root的关系推得。

void InitUnionFind(int n)
{
	for (int i=1; i<=n; ++i)
	{
		p[i]=i;	//初始时每个案件的参照案件是自己
		r[i]=0;	//若参照案件是自己,当然是同一帮派所为
	}
}

#if 0
//非递归版本的find函数。递归版本忘记怎么写了,就写一个非递归版本。
//事实上find就做了一件事:
//1.如果x==p[x],返回x的值
//2.否则,从叶子结点x出发一路向上直到找到树根,并把沿途经过的所有结点(包括x)都指向树根,即参照案件都设置为树根上面的案件,并更新关系。
int myFind(int x)
{
	if(x==p[x]) return x;

	int wp=x;	//wp means waypoints
	stack<int> waypoints;
	while (wp!=p[wp])
	{
		waypoints.push(wp);
		wp=p[wp];
	}	//此while结束后wp就是始祖了

	int cf=wp;	//cf means current father
	int cn;	//cn means current node
	while (!waypoints.empty())
	{
		cn=waypoints.top();
		p[cn]=wp;	//把沿途所有结点指向始祖wp
		r[cn]=(r[cn]+r[cf])%2;	//更新与始祖wp的关系
		cf=cn;	//更新当前父结点
		waypoints.pop();
	}

	return wp;
}
#endif

#if 1
//递归版本的find函数
int myFind(int x)
{
	if(x==p[x]) return x;

	int t=p[x];
	p[x]=myFind(p[x]);
	r[x]=(r[x]+r[t])%2;

	return p[x];
}
#endif

void myUnion(int x, int y)
{
	int fx=myFind(x);
	int fy=myFind(y);

	if (fx!=fy)	//不在同一棵树中才有必要合并嘛。把fy并到fx上去。
	{
		p[fy]=fx;	//fy的父亲是fx
		r[fy]=(2-r[y] + 1 + r[x])%2;	//具体到此题,出现D字样的时候才Union,当然是不同帮派干的了。
	}
}

int main()
{
	int t, n, m;
	char type, char_enter;	
	int c1, c2;
	scanf("%d", &t);

	while (t--)
	{
		scanf("%d%d%c", &n, &m, &char_enter);
		InitUnionFind(n);
		for (int i=0; i<m; ++i)
		{
			scanf("%c%d%d%c", &type, &c1, &c2, &char_enter);	//又傻逼了,还是没把换行符考虑进去

			if (type=='A')
			{
				if (myFind(c1)!=myFind(c2))	//还没并到一棵树里面的,我们无法判断
				{
					printf("Not sure yet.\n");
				}
				else	//已经并入到一棵树里面的,通过c1, c2与root的关系判断c1, c2的关系
				{
					int jval=(r[c1]+2-r[c2])%2;
					if(jval) printf("In different gangs.\n");
					else printf("In the same gang.\n");
				}
			}
			if (type=='D')	//直接union
			{
				myUnion(c1, c2);
			}
		}
	}

	return 0;
}

你可能感兴趣的:(并查集,OpenJudge,POJ1703,关系并查集,抓住它,发现它)