本题来自于《算法设计与分析》课程实验的计蒜客题目,未经授权请勿转载
1 本题涉及到的知识点
2 题目描述
在蒜头君的国度里有个城市,这
个城市间只有
条路把这个
个城市连接起来。现在,蒜头君在第
号城市,他有张该国地图,他想知道如果自己要去参观第
号城市,必须经过的前一个城市是几号城市(假设你不走重复的路)。
3 输入与输出格式
3.1 输入格式
第一行输入一个整数表示测试数据共有
#card=math&code=M%281%5Cle%20M%5Cle%205%29&id=EqXS7)组。
每组测试数据的第一行输入一个正整数#card=math&code=N%281%5Cle%20N%5Cle%20100000%29&id=i7wWd)和一个正整数
#card=math&code=S%281%5Cle%20S%5Cle%20N%29&id=dvHtw),
表示城市的总个数,
表示蒜头君所在城市的编号。
随后的行,每行有两个正整数
#card=math&code=a%2Cb%281%5Cle%20a%2Cb%5Cle%20N%29&id=HUFeg),表示第
号城市和第
号城市之间有一条路连通。
3.2 输出格式
每组测试数据输个正整数,其中,第
个数表示从
走到
号城市,必须要经过的上一个城市的编号。(其中
时,请输出
)。
4 样例
4.1 样例输入
110 11 91 88 1010 38 61 210 49 53 7
4.2 样例输出
-1 1 10 10 9 8 3 1 1 8
5 分析
5.1 问题的转化
本题目提到个城市间只有
条路把这个
个城市连接起来,因此整个地图将构成树,蒜头君所在的城市即为树的根节点。要前往一个城市,必须要经过的城市就是这个城市的父节点。因此,问题转化为:已知一颗树,如何能求出每个结点的父结点?
5.2 求父节点的方法
我们可以使用树的先序遍历对整个树进行遍历,在这个过程中对所有结点的父结点进行记录,从而得出问题的答案。
5.3 树的存储方法
由于本题在输入树的时候,还不知道根结点与其他结点的连接关系,因此只能采用无向图的存储方式来存储,可选的方式为邻接矩阵或者邻接表。
由于本题数据规模较大:#card=math&code=N%281%5Cle%20N%5Cle%20100000%29&id=QwSdo),使用邻接矩阵存储很可能会导致空间超限。因此,使用邻接表的方式最为合适,我们在这里使用vector数组来作为邻接表,记录每个结点相连接的其他所有结点的序号:
vector<int> t[100001]。
5.4 避免重复访问的方法
由于我们在存储树的时候,是按照无向图的方式来存储,并不是只存储了该结点的子结点,而是把所有具有连接关系的结点都进行了存储。所以,如果直接进行先序遍历的话,会导致重复访问节点(A访问B,B访问A……以此循环),出现死循环。因此,我们需要用visit数组来记录是否访问过了某个结点,如果访问过了,则不对它进行遍历。
6 源代码
#include<iostream>#include<vector>using namespace std;int father[100001];void travel(int head, int lasthead, vector<int>* t, int* visit)/*函数功能:递归形式的树的先序遍历,在遍历途中,把每个结点的父节点记录到father数组中。四个参数的含义:head:当前正在遍历的结点序号lasthead:当前正在遍历的结点的父节点序号t:被遍历的树(vector数组,记录了相邻两个结点的连接关系)visit:避免重复访问而设置的数组。由于vector数组t记录的连接关系的双向的,因此,为了避免重复访问,需要使用visit数组检测该结点是否已经遍历过了。*/{father[head] = lasthead;//设置当前结点的父节点是lastheadvisit[head] = 1;//表示该结点已经访问过了for (int i = 0; i < t[head].size(); i++)//遍历所有与当前结点连接的结点{if (!visit[t[head][i]])//如果这个结点还没有被访问过travel(t[head][i], head, t, visit);//使用先序遍历对其进行访问}}int main(){int m;cin >> m;//输入测试数据的个数for (int i = 0; i < m; i++)//遍历每次的测试数据{int n, s;cin >> n >> s;//输入城市个数、蒜头君在哪个城市father[s] = -1;//定义蒜头君所在城市的父结点为-1(无父节点)int visit[100001] = { 0 };//清除访问过的城市vector<int> t[100001];//定义树vector数组for (int j = 0; j < n - 1; j++)//输入n-1组数据{int a, b;cin >> a >> b;//输入连接的两个结点号t[a].push_back(b);//把相应结点号加入到对应vector结点中t[b].push_back(a);//表示a与b连接,b也与a连接}travel(s, -1, t, visit);//对该树进行先序遍历,遍历途中记录每个结点的父节点,存到father数组中。这里传入-1参数的原因是,根节点的父结点为-1for (int j = 1; j <= n; j++)//遍历father数组cout << father[j]<<" ";//打印输出每个结点的父节点}}
7 主函数流程图

