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;
}
hiho_1041 国庆出游的更多相关文章
- Hiho1041 国庆出游 搜索题解
题目3 : 国庆出游 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描写叙述 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比較有特色:它共同拥有n座城市(编号1-n): ...
- hihoCoder 1041 国庆出游 最详细的解题报告
题目来源:国庆出游 解题思路(下面是大神的写的): 把题目中的序列称作S,树称作T.那么对于S中的任意节点x,x的子孙节点如果在S出现的话,那么这个子孙节点的位置是有一定要求的:x的所有子孙节点在S中 ...
- 国庆出游神器:魔幻黑科技换天造物,让vlog秒变科幻大片!
摘要:国庆旅游景点人太多,拍出来的照片全是人人人.车车车,该怎么办?不妨试试这个黑科技,让你的出游vlog秒变科幻大片. 本文分享自华为云社区<国庆出游神器,魔幻黑科技换天造物,让vlog秒变科 ...
- hihoCoder 1041 国庆出游 (DFS)
题意: 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历 ...
- hihocoder——1041国庆出游(搜索)
描述 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历所 ...
- HihoCoder1041 国庆出游 树形DP第四题
小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历所有城市 ...
- 【hihoCoder】1041. 国庆出游
问题:详见http://hihocoder.com/problemset/problem/1041 有n个城市,城市编号为1-n,城市间有n-1条路(所以,城市路网是一棵树).给定一个序列S,要求判断 ...
- [HIHO1041]国庆出游(DFS, bitset)
题目链接:http://hihocoder.com/problemset/problem/1041 学会了用C++的bitset哈,可喜可贺.以后遇到超过64位想用位来表示状态就不愁了哈. 这题用bi ...
- hiboCoder 1041 国庆出游 dfs+思维
先抽象出一棵以1做为根结点的树.给定了访问序列a[1..m]. 考虑两种特殊情况: 1.访问了某个a[j],但是存在a[i]没有访问且i < j,出现这种情况说明a[j]一定是a[i]的祖先节点 ...
随机推荐
- python两个dataframe的合并
见http://pandas.pydata.org/pandas-docs/stable/merging.html
- Python科学计算环境推荐——Anaconda
最近在用Python做中文自然语言处理.使用的IDE是PyCharm.PyCharm确实是Python开发之首选,但用于科学计算方面,还略有欠缺.为此我尝试过Enthought Canopy,但Can ...
- Java中的类加载器
转载:http://blog.csdn.net/zhangjg_blog/article/details/16102131 从java的动态性到类加载机制 我们知道,Java是一种动态语言.那么怎 ...
- IDEA打war包
一 第一步创建一个web application:expolded 选择当前项目 二 新建一个web application: Archive 选择刚刚新建的Expoded “For ‘...... ...
- BFC——一个我们容易忽视掉的布局神器
今天给大家说说BFC这个概念,在说概念前,先给大家看个例子: 首先,定义三个div块元素 效果: 我们发现,块级元素的排列顺序是从上往下,一块接着一块,在w3c中,是这样解释block-lev ...
- BZOJ 1488: [HNOI2009]图的同构 polay
题意:两个图AB同构:把A的顶点重新编号后与B一模一样.求n个顶点的图一共有多少个?(同构的算一种) 思路:边有n*(n-1)/2,这些边可以有可以没有,所以等同于边的颜色有两种.然后将n划分成循环节 ...
- Common Macros for Build Commands and Properties
https://msdn.microsoft.com/en-us/library/c02as0cs.aspx $(ProjectDir) The directory of the proje ...
- linux sudo apt-get用法详解
APT的使用(Ubuntu Linux软件包管理工具一)apt-cache search # ------(package 搜索包)apt-cache show #------(package 获取包 ...
- (Theano 1)Theano自述文件
Theano在GitHub上的自述文件 https://github.com/Theano/Theano 也不知道这个Theano好不好,但是从Theano到Lasagne:基于Python的深度学习 ...
- Timeout expired超时时间已到. 达到了最大池大小 错误及Max Pool Size设置
此文章非原创,仅为分享.学习!!! 参考数据库链接串: <add key="data" value="server=192.168.1.123; port=3306 ...