hiho_1041 国庆出游

题目

给定一棵树,N个节点,N - 1条边。给定m个节点,能否找出一种遍历方法,使得首次到达节点ai的时间小于首次到达节点aj的时间(i < j)。且经过的路径上的每条边都最多走两遍

分析

我的想法: 
    深度优先搜索的策略,在进入某个节点A时,以该节点A为根的子树中的所有节点构成一个集合,该集合内的点将在 m个点集合中连在一块, 
而在节点A为根的子树之外的点不能混在其中。

在实现的时候,定义全局数组 gBlocked[],gBlocked[i] 表示节点i将不能再访问(需要判断并标记), gVisited[i] 表示节点i在当前已经被访问过( 
在之前访问其他节点的路径上被经过)。 
从头到尾遍历m个点,对于点Mi: 
1)如果该节点在之前被访问过,则失败! 
2)找出从Mi向上到达根的路径 path_new,并将路径上的点都标记为 访问过; 
在从节点Mi出发寻找Mi到根节点的路径的时候,如果中间某个节点的状态为“不能在访问”,则失败! 
3)然后,和前一个点Mi-1 形成的路径 path_old 进行比较,找出 Mi 和 Mi-1的最低的公共祖先节点 P. 然后将 从P到Mi-1的路径中,P之后的那个节点t标记 
为 “不能再访问” 状态(gBlocked[t] = true),因为此时已经从节点t的子树中出来了。 
    遍历完所有m个节点之后,返回成功!

参考网上的想法: 
1.预处理,确定每个节点出发可以到达的所有节点。(通过父节点可以到达子节点到达的所有节点进行递归)
2.从根节点开始按照顺序访问m个点,在访问点 Mi 的时候,若当前点为P, 则P的子节点中寻找能够到达 Mi
的节点 Q,然后递归到Q,并将Q标记为“访问过”, 从P找不到可以访问到Mi的子节点,则回溯到P的父节点T, 从T 
再递归

直到访问完所有的节点,或者中间的某个节点递归到根节点仍然无法访问

实现

解法1

/*
深度优先搜索的策略,在进入某个节点A时,以该节点A为根的子树中的所有节点构成一个集合,该集合内的点将在 m个点集合中连在一块,
而在节点A为根的子树之外的点不能混在其中。

在实现的时候,定义全局数组 gBlocked[],gBlocked[i] 表示节点i将不能再访问(需要判断并标记), gVisited[i] 表示节点i在当前已经被访问过(
在之前访问其他节点的路径上被经过)。
从头到尾遍历m个点,对于点Mi:
1)如果该节点在之前被访问过,则失败!
2)找出从Mi向上到达根的路径 path_new,并将路径上的点都标记为 访问过;
	在从节点Mi出发寻找Mi到根节点的路径的时候,如果中间某个节点的状态为“不能在访问”,则失败!
3)然后,和前一个点Mi-1 形成的路径 path_old 进行比较,找出 Mi 和 Mi-1的最低的公共祖先节点 P. 然后将 从P到Mi-1的路径中,P之后的那个节点t标记
为 “不能再访问” 状态(gBlocked[t] = true),因为此时已经从节点t的子树中出来了。

遍历完所有m个节点之后,返回成功!
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<deque>
using namespace std;
#define N 120
struct Edge{
	int to;
	int next;
	Edge(int t = -1, int n = -1) :to(t), next(n){};
};
Edge gEdges[N];
int gEdgeIndex;
int gHead[N];
bool gVisited[N];
int gPre[N];

int gTravelSeq[N];
bool gBlocked[N];

void InsertEdge(int u, int v){
	int e = gEdgeIndex++;
	gEdges[e].to = v;
	gEdges[e].next = gHead[u];
	gHead[u] = e;

	e = gEdgeIndex++;
	gEdges[e].to = u;
	gEdges[e].next = gHead[v];
	gHead[v] = e;
}
//Dfs确定树的结构,因为一开始给的点对不一定是 父--子 ,所以需要遍历图来进行确定树的父子关系
void Dfs(int u){
	gVisited[u] = true;
	for (int e = gHead[u]; e != -1; e = gEdges[e].next){
		int v = gEdges[e].to;
		if (!gVisited[v]){
			gPre[v] = u;
			Dfs(v);
		}
	}
}
//寻找从节点u向上到根节点的路径
bool Path(int u, deque<int>& path){
	path.clear();
	while (u != -1){
		if (gBlocked[u]){
			return false;
		}
		gVisited[u] = true;
		path.push_back(u);
		u = gPre[u];
	}
	return true;
}

//判断是否可以按照顺序遍历这m个节点,n为节点的数目
bool CanTravel(int m, int n){
	if (m <= 0)
		return false;
	if (gTravelSeq[0] > n)
		return false;
	memset(gVisited, false, sizeof(gVisited));
	deque<int> last_path; //保存路径
	bool can_path = Path(gTravelSeq[0], last_path);
	deque<int> path;
	for (int i = 1; i < m; i++){
		int u = gTravelSeq[i];
		if (u > n || gVisited[u]) //节点u在访问之前的节点过程中被访问该,返回失败
			return false;
		can_path = Path(gTravelSeq[i], path);
		if (!can_path) //寻找从节点u到根节点的路径时,遇到了 “不能再访问”的节点,返回失败
			return false;
		int k1 = last_path.size() - 1;
		int k2 = path.size() - 1;
		//寻找最近公共祖先
		while (k1 >= 0 && k2 >= 0 && last_path[k1] == path[k2])
		{
			--k1;
			--k2;
		}
		if (k1 >= 0) //最近公共祖先到 gTravelSeq[i-1]的的下一个点,标记为不能再访问
			gBlocked[last_path[k1]] = true;
		last_path = path;
	}
	return true;
}
void Init(){
	memset(gVisited, false, sizeof(gVisited));
	memset(gPre, -1, sizeof(gPre));
	memset(gEdges, -1, sizeof(gEdges));
	memset(gHead, -1, sizeof(gHead));
	memset(gBlocked, false, sizeof(gBlocked));
	gEdgeIndex = 0;
}
int main2(){
	int T, n, m, u, v;
	scanf("%d", &T);
	while (T--){
		Init();
		scanf("%d", &n);
		for (int i = 0; i < n - 1; i++){
			scanf("%d %d", &u, &v);
			InsertEdge(u, v);
		}
		Dfs(1);
		scanf("%d", &m);
		for (int i = 0; i < m; i++){
			scanf("%d", &(gTravelSeq[i]));
		}
		bool ret = CanTravel(m, n);
		if (ret)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

解法2

/*
参考网上的做法:
1.预处理,确定每个节点出发可以到达的所有节点。(通过父节点可以到达子节点到达的所有节点进行递归)
2.从根节点开始按照顺序访问m个点,在访问点 Mi 的时候,若当前点为P, 则P的子节点中寻找能够到达 Mi
的节点 Q,然后递归到Q,并将Q标记为“访问过”, 从P找不到可以访问到Mi的子节点,则回溯到P的父节点T, 从T
再递归

直到访问完所有的节点,或者中间的某个节点递归到根节点仍然无法访问
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<deque>
#include<bitset>
using namespace std;
#define N 120
struct Edge{
	int to;
	int next;
	Edge(int t = -1, int n = -1) :to(t), next(n){};
};
Edge gEdges[N];
int gEdgeIndex;
int gHead[N];
bool gVisited[N];
int gPre[N];
int gTravelSeq[N];
bitset<N> gCanReach[N]; //判断节点是否可以到达其他节点,用bitset可以方便的进行位操作

void InsertEdge(int u, int v){
	int e = gEdgeIndex++;
	gEdges[e].to = v;
	gEdges[e].next = gHead[u];
	gHead[u] = e;

	e = gEdgeIndex++;
	gEdges[e].to = u;
	gEdges[e].next = gHead[v];
	gHead[v] = e;
}

//确定从u可以到达的所有节点
void CanReach(int u){
	gCanReach[u][u] = 1;
	gVisited[u] = true;
	for (int e = gHead[u]; e != -1; e = gEdges[e].next){
		int v = gEdges[e].to;
		if (!gVisited[v]){ //若为true,则说明v为u的父节点
			gPre[v] = u;
			CanReach(v);
			gCanReach[u] |= gCanReach[v];
		}
	}
}

//当前节点为u,需要 按照顺序遍历的索引为 index, m为需要访问的节点数目
bool CanTravel(int u, int index, int m){
	if (index == m) //所有节点都访问过了
		return true;
	if (u == -1)	//最后无法继续向下访问
		return false;
	gVisited[u] = true;
	if (u == gTravelSeq[index]){	//访问到一个节点,从当前节点开始继续下一个
		return CanTravel(u, index + 1, m);
	}
	for (int e = gHead[u]; e != -1; e = gEdges[e].next){
		int v = gEdges[e].to;
		//从当前节点的子节点中查找之前没有经过的,且能够到达目标节点的
		if (!gVisited[v] && gCanReach[v][gTravelSeq[index]]){
			return CanTravel(v, index, m);
		}
	}
	//回溯到上一层节点
	return CanTravel(gPre[u], index, m);
}

void Init(){
	memset(gVisited, false, sizeof(gVisited));
	memset(gEdges, -1, sizeof(gEdges));
	memset(gHead, -1, sizeof(gHead));
	memset(gPre, -1, sizeof(gPre));
	for (int i = 0; i < N; i++){
		gCanReach[i].reset(0);
	}
	gEdgeIndex = 0;
}
int main(){
	int T, n, m, u, v;
	scanf("%d", &T);
	while (T--){
		Init();
		scanf("%d", &n);
		for (int i = 0; i < n - 1; i++){
			scanf("%d %d", &u, &v);
			InsertEdge(u, v);
		}
		CanReach(1);
		scanf("%d", &m);
		for (int i = 0; i < m; i++){
			scanf("%d", &(gTravelSeq[i]));
		}
		memset(gVisited, false, sizeof(gVisited));
		bool ret = CanTravel(1, 0, m);
		if (ret)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}
时间: 2024-08-07 00:17:17

hiho_1041 国庆出游的相关文章

Hiho1041 国庆出游 搜索题解

题目3 : 国庆出游 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历所有城市,并且经过每一条公路恰好两次--来回各一次--这样公路两旁的景色都不会错过. 令小Hi苦恼的是他的小伙伴小Ho希望能以某种特定的顺序游历其中m个城市.例如按3-2-5的顺序游历这3座城市.(具体来讲是要

1041 : 国庆出游

#1041 : 国庆出游 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历所有城市,并且经过每一条公路恰好两次--来回各一次--这样公路两旁的景色都不会错过. 令小Hi苦恼的是他的小伙伴小Ho希望能以某种特定的顺序游历其中m个城市.例如按3-2-5的顺序游历这3座城市.(具体来讲

国庆出游

国庆出游 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历所有城市,并且经过每一条公路恰好两次——来回各一次——这样公路两旁的景色都不会错过. 令小Hi苦恼的是他的小伙伴小Ho希望能以某种特定的顺序游历其中m个城市.例如按3-2-5的顺序游历这3座城市.(具体来讲是要求:第一次到

hihoCoder 1041 国庆出游 最详细的解题报告

题目来源:国庆出游 解题思路(下面是大神的写的): 把题目中的序列称作S,树称作T.那么对于S中的任意节点x,x的子孙节点如果在S出现的话,那么这个子孙节点的位置是有一定要求的:x的所有子孙节点在S中的位置都恰好紧跟在x的后面,没有被其他节点隔开. 设x的子孙节点是abcd,那么--xabcd--, --xbcda-- 等等是合法的,--xab-cd--, --axbcd--, --x--abcd--, 都是不合法的('-'指其他节点).对于S中每个节点都做如上判断,如果有不合法的就输出NO,如

男子免费请亲戚朋友加油, 结果国庆出游泡汤了!

马某36岁,安徽人,在义乌一家快递公司做快递员. 9月29日晚6点多,他下班准备回到上溪镇四通路的家中,在途径红安路时,忽然发现路边有一张卡. 他凑近一看,是一张中国石化的油卡. 马某忽然想到,国庆一家人正准备出门自驾游,本想这两天去给车子加油的,如今可谓是"天上掉馅饼",正巧砸到了他头上. GIF 当天晚上,马某便喜滋滋地开着车去了上溪义西加油站. 他抱着侥幸心理,把油卡递给了加油站工作人员. 这张卡居然没有设置密码,可以直接使用!工作人员也没有多问,插好卡,就开始加油了. 加满油,

HihoCoder1041 国庆出游 树形DP第四题

小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历所有城市,并且经过每一条公路恰好两次--来回各一次--这样公路两旁的景色都不会错过. 令小Hi苦恼的是他的小伙伴小Ho希望能以某种特定的顺序游历其中m个城市.例如按3-2-5的顺序游历这3座城市.(具体来讲是要求:第一次到达3号城市比第一次到达2号城市早,并且第一次到达2号城市比第一次到达5号城市早). 小

hihocoder 1041 国庆出游 dfs

传送门:hihocoder 1041 给定一棵树,从节点1出发. 如今要求一种遍立方法,使得全部边都来回各走一遍,问在这样的条件下能否按指定顺序訪问部分点 比較巧妙的dfs深搜. 默认1为根节点,因此从1节点出发,预处理出每一个节点的子树中能达到的节点. 然后进行深搜,深搜策略例如以下: 对根节点開始,若找到某一个子节点能到达目标节点,且走向该节点的路径没有訪问过,则走向子节点继续深搜直至按指定顺序訪问全然部节点,或者某一节点已不可达 /*****************************

hihocodr 1041 国庆出游 dfs

传送门:hihocoder 1041 给定一棵树,从节点1出发.现在要求一种遍立方法,使得所有边都来回各走一遍,问在这种条件下是否能按指定顺序访问部分点 比较巧妙的dfs深搜.默认1为根节点,因此从1节点出发,预处理出每个节点的子树中能达到的节点.然后进行深搜,深搜策略如下: 对根节点开始,若找到某一个子节点能到达目标节点,且走向该节点的路径没有访问过,则走向子节点继续深搜直至按指定顺序访问完所有节点,或者某一节点已不可达 /***********************************

情侣国庆游丽江当天分手,环保专家怎么看?

天有不测风云,人有旦夕祸福,没有人知道未来的自己将会面对什么样的困境.前段时间在网上闹的纷纷扬扬的分手事件,相信很多朋友对此多多少少都会有些耳闻,也有着自己的见解.对于此,环保专家林铮表示,可以看出该女子较为支持环保公益,出游时对于男友的看法看不惯可以理解,但是,却不应该以强硬的态度劝其整改,应该循序渐进的进行引导,效果可能更好一些. 图:引发分手事件的公益活动 林铮表示,第一次听到这个消息的时候他也很是好奇,想知道事件中女主角的真实想法.尽管她的做法并不可取,但现代年轻人能够如此支持环保事业,