描述
小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
分析:
在给定的序列中,对于其中的一个ai,如果在序列中存在ai 的子树节点,
那么所有ai的子树节点,必须连续紧跟在ai的后面!!!
思路:
深搜去查找,如果规定的序列中下一个需要出现的节点是当前搜索的点的子节点,就一步步深入,并记录走过的路径
这里用了<bitset> 来非常巧妙的记录一个点的所有字节点
比如:child[105][105], child[1~105]每个都 有 105 个二进制位,若child[1][4] = 1,
也就是说 4 是 1 的子节点
最后判断能否按顺序深搜完规定序列中的节点
对于 什么每条边来回两次这个问题,
因为是一个最小生成树,正因为有了这个条件,才得出,若想实现规定的顺序,那么必须,
对于规定序列的一个ai,如果在序列中存在ai 的子树节点,
那么所有ai的子树节点必须连续紧跟在ai的后面。。。。
#include <iostream> #include <algorithm> #include <vector> #include <bitset> #include <cstring> using namespace std; const int MAXN = 100 + 5; int book[MAXN][MAXN]; int task[MAXN]; int m; int cnt; // 判断能否完成任务 int flag; // 用来判断能否完成任务 vector<int> T[MAXN]; // tree bitset<MAXN> child[MAXN]; // 巧妙用2进制位记录每个节点的所有子节点 // 声明函数 void inputT(int n); void init(); void getchild(int fv, int v); void solve(int fv, int v); // 主函数 int main() { ios::sync_with_stdio(false); //t组测试数据 int t; cin >> t; while (t--) { // 初始化 init(); //n 个节点 int n; cin >> n; //读入最小生成树 inputT(n - 1); //获得每个节点的子节点 getchild(-1, 1); // 解决 solve(-1, 1); // 判断flag if (flag) cout << "YES" << endl; else cout << "NO" << endl; } // system("pause"); return 0; } // 初始化 void init() { flag = 0; cnt = 0; memset(book, 0, sizeof(book)); memset(task, 0, sizeof(task)); for (int i = 0; i < MAXN; i++) { T[i].clear(); // 所有二进制位归零 child[i].reset(); } } // 读入树 void inputT(int n) { for (int i = 0; i < n; i++) { int v1, v2; cin >> v1 >> v2; //因为没有确定v1 v2的大小关系,所以要双向读入 T[v1].push_back(v2); T[v2].push_back(v1); book[v1][v2] = 1; book[v2][v1] = 1; } cin >> m; for (int i = 0; i < m; i++) cin >> task[i]; } // 查找并记录每个节点的子节点 // 巧妙运用二进制位来记录子节点, // 比如:child[1] 有 105 个二进制位,若chile[1][4] = 1, // 也就是说 4 是 1 的子节点 void getchild(int fv, int v) { child[v][v] = 1; for (int i = 0; i < T[v].size(); i++) { int v1 = T[v][i]; // 因为存树的问题,v1也有可能是父节点 if (v1 != fv) child[v][v1] = 1; else if (v1 == fv) continue; // DFS getchild(v, v1); child[v] = child[v] | child[v1]; } return; } void solve(int fv, int v) { // 下面两句if的顺序不能反 if (v == task[cnt]) cnt++; if (cnt == m) { flag = 1; return; } while (cnt < m) { int next_v = task[cnt]; int p = cnt; for (int i = 0; i < T[v].size(); i++) { int v1 = T[v][i]; if (fv == v1) continue; if (child[v1][next_v] == 1 && book[v][v1]) { book[v][v1] = 0; //防止重复走过 solve(v, v1); } } // 如果没有这一句判断,无法出while if(p == cnt) break; } return; }