神秘国度的爱情故事

[问题描述]

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

[基本要求]

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

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

解题思路

问题用到最近公共祖先算法,求C是否在A和B的路径上

村落分布可抽象化为图,用邻接表保存,定义两个结构Edge和Node,Edge用于保存节点的邻接节点,里面还有指向下一个链头节点邻接节点的指针,Node里面定义了节点的编号,节点的深度(遍历时初始化,用于寻找最近公共祖先时先使指向深层的节点指针往上跳到与浅层节点指针同一层次),节点的父亲数组fa[],保存该节点路径上的父亲节点。找最近公共祖先的时候,如果两节点在同一层次时,两级点相等,那么该节点就是最近公共节点,否则,设村庄A与村庄B的最近公共祖先为D,A和C的最近公共祖先为AC,B和C的最近公共祖先为BC,如果ACC或者BCC,这里需分情况讨论,如果C==D,那么C就是最近公共祖先,C在AB的路径上,否则说明C不是A和B的最近公共祖先,C是C和A的最近公共祖先或者是B和C的最近公共祖先,C在AD路径上或在BD路径上,如果C并不是A的最近公共祖先并且不是B的最近公共祖先,那么B不是A和B路径上的点

LCA中倍增法

将2的次幂排成一个序列,1 、2 、4 、8~,任何数都可以用其中的几个数相加得到,比如20=16+4,也就是每个正整数都可以拆分成多个2的幂次数。

这样就可以使两个结点不再一层一层地往上跳,而是每次都跳2的幂次方层。例如a点要向上跳10层,就可以跳两次:先跳8层,再跳2层。

有数组tv[u].fa[i],表示下标为u的节点向上跳2^i 个节点,等于跳了2^(i-1) 个节点再加2^(i-1) 个节点,所以tv[u].fa[i]=tv[tv[u].fa[i-1]].fa[i-1],有了这个结论就可以得到每个节点向上跳2^i 格,这个初始化在BFS中完成,在LCA中,先把两个不同层次的节点跳到同一节点,就运用了倍增思想,先求他两节点的层次差,用2的次幂表示(本题中用到的处理方法是转换成二进制,变量i表示二进制第几位,第i位为1则跳到他的第2^ i父亲节点那里,为0则不跳同时i自增,fa[i]是第2^ i个父亲),再结合BFS中初始化的各节点保存的父亲数组,就可以跳到任意下标的节点了

代码实现

//LCA.hlude<iostream>#include<queue>#define Height 20//因为最多有50000个村庄,所以log2(50000)=20extern int num, line;using namespace std;typedef struct Edge {//边节点int adjnode;//邻接点的下标Edge* next;//指向下一个节点}Edge;typedef struct Node {int ID;int fa[Height];//用于保存节点的父亲节点,方便找共同祖先int depth;//保存节点的深度,用于判断两个节点的层次关系,深层的要先往上跳至浅层的,两层再一起网上跳找共同祖先Edge* firstNode;//邻接表中表头指向第一个节点用}Node;//创建邻接表void CreateGraph(Node*& node, int n);//广度优先遍历,初始化该图,找到每个节点的父亲和所在的深度void BFS(Node*& node, int root);//寻找最近公共祖先int LCA(Node* node, int u, int v);//判断节点是否在两节点的路径上void Search(Node* node, int a, int b, int c);//LAC.cpp#include "LCA.h"int num = 0, line = 0;void CreateGraph(Node*& node, int n){int u, v;Edge* p;num = n;//村庄数line = n - 1;//行数for (int i = 0; i < num;
i++) {node[i].ID = i;//初始化头节点数组,每个小村从0~n-1编号node[i].firstNode = NULL;//每个头节点的第一个邻接点为空}cout << "请输入村子的" << line << "条路" << endl;for (int i = 0; i < line;
i++) {cin >> u >> v;//输入两个村庄p = new Edge;//构建边结点p->adjnode
= v;p->next =
node[u].firstNode;node[u].firstNode = p;//头插进去p = new Edge;p->adjnode
= u;//节点间是双向的p->next =
node[v].firstNode;node[v].firstNode = p;}}void BFS(Node*& node, int root){int u;//暂存队首节点的下标int v;//暂存下一邻接节点的下标node[root].fa[0] = root;//根节点的父亲是他自己node[root].depth = 0;//根节点的深度为0queue<int>que;//队列,每访问一个节点,该节点进队列que.push(root);while (!que.empty()) {u =
que.front();que.pop();//将队首节点出队列for (int i = 1; i < Height; i++)//寻找节点的所以父亲节点,据u的第2^i个父亲结点等于u的第2^(i-1)个父亲结点的第2^(i-1)个父亲结点node[u].fa[i] = node[node[u].fa[i - 1]].fa[i
- 1];Edge* p;p = node[u].firstNode;while (p != NULL) {v =
p->adjnode;if (v == node[u].fa[0]) {p =
p->next;//指针指向下一个同一层的节点}else {//因为存储的是双向边,所以防止再访问到已经访问过的父亲结点node[v].depth = node[u].depth + 1;//节点深度为父亲节点深度加1node[v].fa[0] = u;//记录v的父亲节点下标que.push(v);//将v入队列}}}}int LCA(Node* node, int u, int v){if (node[u].depth > node[v].depth)swap(u, v);//使u存放较浅层次的节点,v存放较深层次的节点int hu = node[u].depth, hv = node[v].depth;int tu = u, tv = v;//游走找最近公共祖先for (int dec = hv - hu, i =
0; dec; dec >>= 1, i++)//使两个节点在同一层次if (dec & 1)//层次差相与为1的时候代表该为对应十进制中的i,这里的i是循环增大的,每一次循环对应于差的二进制后几位的和tv = node[tv].fa[i];//通过数制之间的关系减少移动次数if (tu == tv)//如果两节点在同一层次,且他们相等,他们tu即为他们的公共祖先return tu;for (int i = Height - 1; i
>= 0; i--) {if (node[tv].fa[i] == node[tu].fa[i])//如果一不小心跳过头了,则跳过此步,虽然层次可能没有9层,但超出的部分fa[i]都是等于0的,continue;tv = node[tv].fa[i];tu = node[tu].fa[i];//两节点下标同时往上跳,找最近公共祖先}return node[tu].fa[0];//返回最近公共祖先}void Search(Node* node, int a, int b, int c){int d = LCA(node, a, b);//寻找a,b的最近公共祖先int ac = LCA(node, a, c);int bc = LCA(node, b, c);bool flag;if (ac == c && bc == c) {//如果c是a,b的公共祖先if (c == d)//如果c是a,d的最近公共祖先flag = true;else//如果c不是a,b的最近公共祖先flag = false;}else if (ac == c || bc == c) {//c是a的祖先或者是b的祖先,说明c在a到d的路径上或者在b到d的路径上flag = true;}else//如果c不是a的祖先,也不是b的祖先,则a和b的路径上不会经过c点flag = false;if (flag)cout << "YES..." << endl;elsecout << "NO..." << endl;}// Curriculum_design.cpp #include <iostream>#include"LCA.h"int main(){  int a, b, c;int N, M;cout << "请输入村子数:";cin >> N;Node* node = new Node[N +1];//动态创建邻接表的头结点数组CreateGraph(node,
N);//创建邻接表BFS(node, 0);//初始化邻接表,使每个节点都保存有其路径上的所以父亲节点的下标和他的访问深度(也就是在二叉树中的层数)cin >> M;//输入测试用例的个数for (int i = 0; i < M;
i++) {cin >> a >> b >> c;Search(node,
a, b, c);//检查c是否在a,b的路径上}}

测试数据

13

1 2

1 3

1 4

2 5

3 6

6 10

10 13

6 11

4 7

4 8

8 12

4 9

6

13 11 6

7 12 8

12 13 3

10 11 3

5 6 3

2 3 7

程序运行结果

神秘国度的爱情故事——广州大学课程设计相关推荐

  1. 神秘国度的爱情故事--数据结构课程设计

    结论:最开始N个结点的树,处理M组数据,采用深度优先搜索,总时间复杂度为O(NM).优化方法是找最近公共祖先(lca)的倍增法.N个结点的树,每次找最近公共祖先的时间复杂度为O(logN),处理M组数 ...

  2. 【广州大学】数据结构课程设计:神秘国度的爱情故事

    数据结构课程设计报告 广州大学 计算机科学与网络工程学院 计算机系 19级网络工程专业网络194班 超级菜狗 (学号:19062000) (班内序号:xxx) 完成时间:2021年1月11日 一.课程 ...

  3. 数据结构课程设计 神秘国度的爱情故事

    数据结构 课程设计报告 广州大学 计算机科学与网络工程学院 计算机系 17级计科专业2班 2019年6月30日 广州大学学生实验报告 开课学院及实验室:计算机科学与工程实验室              ...

  4. 神秘国度的爱情故事 数据结构课设-广州大学

    神秘国度的爱情故事 数据结构课设-广州大学 ps:本次课设程序不仅需要解决问题,更需要注重代码和算法的优化和数据测试分析      直接广度优先实现的方法时间复杂度为O(QN),优化后的方法是lca+ ...

  5. 数据结构课程设计-神秘国度的爱情故事-LCA:tarjan+离线/树链剖分/暴力

    1.无脑暴力dfs:   O(n*m) 2.LCA/tarjan+离线处理: O(n+m) 3.LCA/树链剖分: O(nlogn+m)~O(nlogn+mlogn) 4.LCA/倍增思想(有空再补) ...

  6. 敏捷的Scrum用户体验设计爱情故事

    For those who are working in a software development environment, the word "Agile" is omnip ...

  7. 广州大学软件方向综合课程设计报告(专业课程数据库系统,模拟一个学期选课退课)带智能排课算法(遗传算法)

    广州大学软件方向综合课程设计目录 序章 第一章 系统需求简介 1.1 需求分析 1.2 数据结构需求分析 1.3系统功能设计 第二章 需求描述 2.1 数据流图 2.2 数据字典 第三章 概念设计 3 ...

  8. 广州大学2021计算机组成原理课程设计实验报告

    一.本课程设计的性质.目的.任务 <计算机组成与系统结构课程设计>是计算机学院各专业集中实践性环节之一,是学习完<计算机组成与系统结构>课程后进行的一次全面的综合练习.其目的是 ...

  9. 广州大学数据结构课程设计

    程序设计题目 交通查询系统设计(难度系数 1.5) [问题描述] 今天铁路交通网络非常发达,人们在出差.旅游时,不仅关注交通费用,还关注里程和时间.请按照下图设计一个交通查询系统,能够满足旅客查询从任 ...

最新文章

  1. matlab奈馈斯图,matlab关于控制的设计单位负反馈的校正
  2. linux c 内存操作函数 简介
  3. Mysql 死锁过程及案例详解之插入意向锁与自增锁备份锁日志锁Insert Intention Lock Auto-increment Lock Backup Lock Log Lock
  4. 土的液塑限计算机自动图形生成,土样液塑限自动
  5. 1716. 计算力扣银行的钱
  6. 电机编码器调零步骤_蒂森电梯编码器整定和主机整定大全
  7. mysql5.7空间运算_msyql5.7数据类型和运算符
  8. 【转】用Qt生成dll类库及调用方法
  9. PyTorch 深度学习:34分钟快速入门——自动编码器
  10. Python实战从入门到精通第十三讲——返回多个值的函数
  11. 正则表达式验证IP和端口格式的正确性
  12. 学习bind源代码,比较bind的方式绑定函数在在内存使用上优于箭头函数
  13. rsync使用(二)
  14. viewpager 获取当前现实的view
  15. python 新手常见问题
  16. 将mysql驱动包添加到项目依赖
  17. 物联网智能电影院- Android
  18. android日期时间控件
  19. Activiti7 25张表含义
  20. 增值税怎么用计算机算,增值税计算器

热门文章

  1. 音频声学相关的常用缩略语
  2. 修改Echarts源码实现柱状图的炫彩闪烁效果
  3. curl常用参数详解及示例
  4. vue2.0分页插件官方_Vue 2的最佳和完整分页插件
  5. PowerDesigner 16逆向工程,MySQL数据库的生成PDM物理数据模型文件
  6. 【Distilling】《Distilling the Knowledge in a Neural Network》
  7. 一文讲懂什么是 vlan、三层交换机、网关、DNS、子网掩码、MAC地址
  8. 【漏洞学习——SQL】华图教育某分站SQL注入漏洞
  9. 加密与解密 入侵检测 扫描与抓包
  10. 基于SSM的快递代取管理系统