神秘国度的爱情故事

        某个太空神秘国度中有很多美丽的小村,从太空中可以想见,小村间有路相连,更精确一点说,任意两村之间有且仅有一条路径。小村 A 中有位年轻人爱上了自己村里的美丽姑娘。每天早晨,姑娘都会去小村 B 里的面包房工作,傍晚 6 点回到家。年轻人终于决定要向姑娘表白,他打算在小村 C 等着姑娘路过的时候把爱慕说出来。问题是,他不能确定小村 C 是否在小村 B 到小村 A 之间的路径上。你可以帮他解决这个问题吗?

        输入要求:输入由若干组测试数据组成。每组数据的第 1 行包含一正整数 N ( l 《 N 《 50000 ) , 代表神秘国度中小村的个数,每个小村即从0到 N - l 编号。接下来有 N -1 行输入,每行包含一条双向道路的两个端点小村的编号,中间用空格分开。之后一行包含一正整数 M ( l 《 M 《 500000 ) ,代表着该组测试问题的个数。接下来 M 行,每行给出 A 、 B 、 C 三个小村的编号,中间用空格分开。当 N 为 O 时,表示全部测试结束,不要对该数据做任何处理。

        输出要求:对每一组测试给定的 A 、 B 、C,在一行里输出答案,即:如果 C 在 A 和 B 之间的路径上,输出 Yes ,否则输出 No。

示例代码如下:

#include <iostream>
using namespace std;

#define MAXSIZE 50000	
using namespace std;
//村子树节点,数据域有入栈出栈时间域、村子编号域
//树采用孩子兄弟链存储结构
typedef struct Village
{
	int num;		//村子编号
	int inid;		//深度优先遍历入栈记录
	int outid;	//节点出栈记录
	struct Village *firstchild;
	struct Village *nextcousin;
}VillageNode;
typedef struct
{
	int top;
	VillageNode *villages[MAXSIZE];
}Stack;

Stack *s = (Stack *)malloc(sizeof(Stack));
int id = 0;		//记录栈访问编号

void pushStack(VillageNode *p);		//将*p节点压入栈
void popStack(VillageNode *p);		//将栈顶元素弹出,并将地址保存至p指针
VillageNode* dfsfind(VillageNode* root, int va, int vb);//深度查找从A村到B村的路径
int addNode(VillageNode *root, int a, int b);	//增加结点间路径
void dfs(VillageNode *root);					//深度递归遍历
void ifwait(int va, int vb, int vc);			//二分查找

//将*p节点压入栈
void pushStack(VillageNode *p)	//压栈
{
	s->villages[++s->top] = p;
	id++;			//栈访问次数加1
	p->inid = id; //记录入栈
}

//将栈顶元素弹出,并将地址保存至p指针
void popStack(VillageNode *p)		//出栈
{
	if (s->top > -1)
	{
		p = s->villages[s->top];
		s->top--;
		id++;		//栈访问次数加1
		p->outid = id;
	}
}
VillageNode* dfsfind(VillageNode* root, int va)
{
	if (root == NULL)					//当前节点 为空,返回NULL
		return NULL;
	int v = root->num;
	VillageNode *p, *q;
	if (va == v)						//如果找到原先既有的节点,则返回该节点的指针
		return root;
	q = dfsfind(root->firstchild, va); //递归访问孩子节点
	if (q != NULL)
		return q;


	p = root->nextcousin;
	while (p != NULL)
	{
		q = dfsfind(p, va);
		if (q != NULL)
			return q;
		p = p->nextcousin;
	}
	return NULL;			//当访问过的节点达到总数仍没有找到va 或 vb节点时,返回NULL
}

int addNode(VillageNode *root, int va, int vb)
{
	VillageNode *vp = dfsfind(root, va); //寻找编号为a的村子
	VillageNode *p, *q;
	if (vp != NULL)
	{
		if (vp->num == va)			//找到村子A,遍历村子A孩子节点
		{
			p = vp->firstchild;
			q = vp;
			if (p == NULL)			//当vp节点下没有孩子节点时
			{
				VillageNode *vn = (VillageNode*)malloc(sizeof(VillageNode));
				vn->firstchild = NULL;
				vn->nextcousin = NULL;
				vn->num = vb;
				q->firstchild = vn;
				return 1;
			}
			while (p != NULL)
			{
				if (p->num == vb) //当村子A存在到村子B的路时返回0
					return 0;
				q = p;			//记录当前节点
				p = p->nextcousin; //p指向下一个节点
			}
			VillageNode *vn = (VillageNode*)malloc(sizeof(VillageNode));
			vn->firstchild = NULL;
			vn->nextcousin = NULL;
			vn->num = vb;
			q->nextcousin = vn;
			return 1;
		}
	}
	return -1;
}
void dfs(VillageNode *root)		//深度遍历
{
	pushStack(root);
	VillageNode *p;
	p = root->firstchild;		//指向第一个孩子
	while (p != NULL)		//孩子节点不为空,递归访问孩子节点
	{
		dfs(p);
		p = p->nextcousin;
	}
	popStack(p);		//弹出栈顶元素
	return;
}

void ifwait(VillageNode *root, int va, int vb, int vc)
{
	VillageNode *VA = dfsfind(root, va);
	VillageNode *VB = dfsfind(root, vb);
	VillageNode *VC = dfsfind(root, vc);
	if (!VA || !VB || !VC) {
		cout << "A村、B村或者C村不存在!" << endl;
		return;
	}
	cout << "找到的A,B,C分别为:\t" << VA->num<<'\t'<< VB->num << '\t' << VC->num << '\t';
	if (va == vc || vb == vc) {
		cout << "YES" << endl;
	}
	else if ((VC->inid < VA->inid && VC->outid > VA->outid) && (VC->inid < VB->inid && VC->outid > VB->outid)) //C节点的入出栈区间包含A、B的区间
	{//当VC是VA和VB的公共祖先时 判断是否是最低公共祖先

		int a[2][MAXSIZE];											//二维数组第一行存放inid 第二行存放outid 每一列对应一个节点 从左到右
		int min, max;					//min:VA、VB入栈最小	max:VA、VB出栈最大
		min = (VA->inid < VB->inid) ? VA->inid : VB->inid;			
		max = (VA->outid > VB->outid) ? VA->outid : VB->outid;		
		VillageNode *p = VC->firstchild;
		int i = 0;
		while (p != NULL)											//遍历VC节点所有的孩子节点 并把孩子们的入出栈编号存入数组
		{
			a[0][i] = p->inid;
			a[1][i] = p->outid;
			i++;
			p = p->nextcousin;
		}
																	//二分查找[min,max]区间是否包含在其中一个孩子节点中
		int low = 0, high = i - 1, mid;
		while (low <= high)
		{
			mid = (low + high) / 2;
			if (a[0][mid] <= min && a[1][mid] >= max)				//查找到有区间包含[min,max]区间时,VC不是VA和VB的最低公共祖先,跳出循环
			{
				cout << "No" << endl;
				return;
			}
			if (a[0][mid] > min)									//继续在a[0][low...mid-1]中查找
				high = mid - 1;
			else													//继续在a[0][mid+1...high]中查找
				low = mid + 1;
		}
		cout << "YES" << endl;										//当查找结束仍没有找到该区间,说明VC是VA、B的最低公共祖先
	}
	else if ((VC->inid < VA->inid && VC->outid > VA->outid) || (VC->inid < VB->inid && VC->outid > VB->outid)) //或VC节点的入出栈时间区间包含VB的区间
	{
		//VC是VA、VB其中一个节点的祖先,存在A-C-B的路径
		cout << "YES" << endl;
	}
	else //当VC不是VA的祖先而且不是VB节点的祖先时,不存在A-C-B的路径
		cout << "No" << endl;
	return;
}
int main()
{
	cout << "——————————————————————————————" << endl;
	cout << "\t\t神秘国度的爱情故事" << endl;
	cout << "——————————————————————————————" << endl;
	int N, M,a,b;
	int testA[MAXSIZE], testB[MAXSIZE], testC[MAXSIZE];				//分别存放A,B,C测试组
	printf("请输入小村个数:\n");
	cin >> N;
	VillageNode *root = (VillageNode *)malloc(sizeof(VillageNode)); //建立指向深度优先搜索树的指针并建立深度优先搜素树
	root->firstchild = NULL;
	root->nextcousin = NULL;
	root->num = 0;
	printf("接下来,请依次输入包含一条双向道路的两个端点小村的编号:\n");
	for(int i = 0; i < N-1; i++)
	{
		cin >> a >> b; //读取边信息
		int flag = addNode(root, a, b);
		if (flag == 0)
		{
			cout << "小村" << a << "到小村" << b << "的路径已存在,无需再次输入,本次输入将不会被记录。" << endl;
			N++;
		}
		else if (flag == 1){}
		else
			cout << "录入失败!" << a << b << endl;
	}
	s->top = -1;
	dfs(root);				//深度遍历一遍树
	cout<<"请输入A,B,C样例个数:\n";
	cin>>M;
	cout << "请依次输入A,B,C样例:\n";
	for (int i = 0; i < M; i++) {			//用三个数组分别存储各个对应的A、B、C
		cin >> testA[i] >> testB[i] >> testC[i];
	}
	for(int i = 0; i < M; i++)
	{
		ifwait(root, testA[i], testB[i], testC[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40849588/article/details/81051497