首页 > 代码库 > Hiho1041 国庆出游 搜索题解

Hiho1041 国庆出游 搜索题解

题目3 : 国庆出游

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi和小Ho准备国庆期间去A国旅游。A国的城际交通比较有特色:它共有n座城市(编号1-n);城市之间恰好有n-1条公路相连,形成一个树形公路网。小Hi计划从A国首都(1号城市)出发,自驾遍历所有城市,并且经过每一条公路恰好两次——来回各一次——这样公路两旁的景色都不会错过。


令小Hi苦恼的是他的小伙伴小Ho希望能以某种特定的顺序游历其中m个城市。例如按3-2-5的顺序游历这3座城市。(具体来讲是要求:第一次到达3号城市比第一次到达2号城市早,并且第一次到达2号城市比第一次到达5号城市早)。


小Hi想知道是否有一种自驾顺序满足小Ho的要求。

输入

输入第一行是一个整数T(1<=T<=20),代表测试数据的数量。

每组数据第一行是一个整数n(1 <= n <= 100),代表城市数目。

之后n-1行每行两个整数a和b (1 <= a, b <= n),表示ab之间有公路相连。

之后一行包含一个整数m (1 <= m <= n)

最后一行包含m个整数,表示小Ho希望的游历顺序。

输出

YES或者NO,表示是否有一种自驾顺序满足小Ho的要求。

样例输入
2
7
1 2
1 3
2 4
2 5
3 6
3 7
3
3 7 2
7
1 2
1 3
2 4
2 5
3 6
3 7
3
3 2 7
样例输出
YES
NO
好吧,第一次写hiho的题目,一个新起的oj网站。

这次是微软的预测赛。网站有点偷懒啊,用了以前的题目拼凑起来的一个比赛。不过目前网站的题量本来就不多。


本题是第三题,本来应该不难,就是一个深度搜索的题目。哎,我悲剧了,还是没能按时完成,最后还是参考了一下网上程序,自己写了个。看来自己火候还是不够,难怪进不了Google面试,好好继续努力吧。


目前在找工作,想找技术管理这块工作,真忧虑自己做不成自己想做的东西。

一个不知名的学校真有这么大的不便,令人意想不到啊;经历了,又会觉得情理之中,一切都是人性,心态放开点吧。

虽然亚历山大,还是需要继续坚持学习的。

拒了网易运营笔试,拒了聚美什么的笔试,你们要找名校就不要找我玩了,真不想陪你们玩了;面试官的算法题直接被我完美秒杀,连一个小bug你都找不到,都可以拒我的, I服了you!

既然我学校不知名,那我就去小点不那么知名的公司吧,以我的热诚,我相信我可以!


这是一个挺有技巧的深度搜索,需要优化,而且是利用位运算优化,非常巧妙。

主要的思想是一个消除查找的思想,就是给出两个集合,求交集的算法的高级应用。

求交集的思想很简单,就是排序,然后一遍搜索就可以找出了。


这里是在树上求可行的一个序列集合:

加速:这里的getChild函数就是预处理一个节点下面有什么子节点,处理完之后就可以在O(1)的时间内可以判断有没有一个特定的子节点。

然后eliminateAll就是上面说的消除查找思想,代码不长,不过其中有很多细节点,需要好好认真考究,其中的关键点代码中标注出来了。


#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <limits.h>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
using namespace std;

const int MAX_N = 101;
vector<int> graAdj[MAX_N];//邻接表
int arr[MAX_N];
int graMat[MAX_N][MAX_N];//矩阵

bitset<MAX_N> hasChild[MAX_N];
void getChild(int u = 1, int par = -1)
{
	hasChild[u][u] = 1;	//自己是自己的孩子?把自己也并进孩子集合中。
	for (int i = 0; i < (int)graAdj[u].size(); i++)
	{
		int v = graAdj[u][i];
		if (v == par) continue;//防止回路,不回走父亲节点
		getChild(v, u);
		hasChild[u] |= hasChild[v];//巧妙利用bitset加速判断一个节点有多少孩子
	}
}

//利用全局变量graMat和graAdj
bool eliminatAll(int a[], int &id, int m, int u = 1, int par = -1)
{
	if (id < m && a[id] == u) id++;
	if (id == m) return true;

	while (id < m)//关键点,可以循环查找子节点
	{
		int nexta = a[id];
		int curIndex = id;
		for (int i = 0; i < (int)graAdj[u].size(); i++)
		{
			int v = graAdj[u][i];
			if (v == par) continue;//排除父节点,防止回路

			if (hasChild[v][nexta] && graMat[u][v])
			{
				graMat[u][v] = 0;//关键点:拆桥,不用重复查找路径
				if (eliminatAll(a, id, m, v, u)) return true;
			}
		}
		//关键点:防止无限循环,没有答案的时候退出
		if (id == curIndex) break;//没有找到一个对应的点a[id],返回上一层
	}
	return false;
}

int main()
{
	int T, n, a, b, m;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
		{
			graAdj[i].clear();
			hasChild[i].reset();
		}
		memset(graMat, 0, sizeof(graMat));

		for (int i = 1; i < n; i++)
		{
			scanf("%d %d", &a, &b);
			graAdj[a].push_back(b);
			graAdj[b].push_back(a);
			graMat[a][b] = graMat[b][a] = 1;
		}

		scanf("%d", &m);
		for (int i = 0; i < m; i++)
		{
			scanf("%d", arr+i);
		}

		getChild();

		int id = 0;
		if (eliminatAll(arr, id, m)) puts("YES");
		else puts("NO");
	}
	return 0;
}



Hiho1041 国庆出游 搜索题解