一:问题
1.问题描述
给定一个公司的网络,由n台交换机和m台终端电脑组成,交换机与交换机、交换机与电脑之间使用网络连接。交换机按层级设置,编号为1的交换机为根交换机,层级为1。其他的交换机都连接到一台比自己上一层的交换机上,其层级为对应交换机的层级加1。所有的终端电脑都直接连接到交换机上。
当信息在电脑、交换机之间传递时,每一步只能通过自己传递到自己所连接的另一台电脑或交换机。请问,电脑与电脑之间传递消息、或者电脑与交换机之间传递消息、或者交换机与交换机之间传递消息最多需要多少步。
2.格式
输入格式
输入的第一行包含两个整数n, m,分别表示交换机的台数和终端电脑的台数。
第二行包含n - 1个整数,分别表示第2、3、……、n台交换机所连接的比自己上一层的交换机的编号。第i台交换机所连接的上一层的交换机编号一定比自己的编号小。
第三行包含m个整数,分别表示第1、2、……、m台终端电脑所连接的交换机的编号。
输出格式
输出一个整数,表示消息传递最多需要的步数。
3.样例一
样例输入
4 2
1 1 3
2 1
样例输出
4
样例说明
样例的网络连接模式如下,其中圆圈表示交换机,方框表示电脑:
其中电脑1与交换机4之间的消息传递花费的时间最长,为4个单位时间。
4.样例二
样例输入
4 4
1 2 2
3 4 4 4
样例输出
4
样例说明
样例的网络连接模式如下:
其中电脑1与电脑4之间的消息传递花费的时间最长,为4个单位时间。
5.评测用例规模与约定
前30%的评测用例满足:n ≤ 5, m ≤ 5。
前50%的评测用例满足:n ≤ 20, m ≤ 20。
前70%的评测用例满足:n ≤ 100, m ≤ 100。
所有评测用例都满足:1 ≤ n ≤ 10000,1 ≤ m ≤ 10000。
二:理解
题意理解:
1.给的n与m比较好理解,表示交换机的台数和终端电脑的台数。其实就是说明点的个数。(仔细理解你会先发现,无论是终端还是交换机可以看成一个东西就可以,都是可以经过的点)
注:编号为1的交换机为根交换机,这个点是确定的了。
2.接着输入的n-1个数的理解:通俗就是,给定的i(所处的位置)的父节点,从2开始。
第2、3、……、n台交换机所连接的比自己上一层的交换机的编号。
拿例子说话:
1 1 3
1的意思就是,节点2 的父节点是1;
1的意思就是,节点3 的父节点是1;
3的意思就是,节点4 的父节点是3;
同理,接下来的m个数的意思就是:编号为i终端的父节点;
解题思路:
这是一个树的问题,求树的直径,即在树中找出两个结点,使得这两个结点间的距离最长,这个最长距离称为直径。一般可以用两次DFS或BFS来实现,在树上任意选取1个结点s,先用DFS或BFS找到距离s距离最远的结点node,然后再从结点node开始,再次用DFS或BFS找到距离s距离最远的结点,得到结果。
递归式的DFS:
代码解释:
1.数据结构
vector<int> num[20005];
int visited[20005] = {
0}; //标记是否走过
STL的向量数组vector<int> num[20005]
来存储各点之间的联系:
for(int i=2; i<=n+m; i++)
{
cin >> father;
//表示i与father建立联系
num[i].push_back(father);
num[father].push_back(i);
}
2.先是通过BFS(1,0);来找出最长的这个数枝,记住最后这个节点的编号。
3.接着从这个最后的这个节点,在进行一次BFS(node, 0);,这样得到的长度就是最多的步数(最后的结果)。
length = -1;
DFS(1,0); //起点是1,起始路程为0,目的是找到最远的那个点
length = -1;
memset(visited, 0, sizeof(visited));
DFS(node, 0); //从最远的这个点,再找最远的点就是所求的长度。
4.着重了解的是这个最后的递归式的BFS(int startnode, int templen);
void DFS(int startnode, int templen)
{
visited[startnode] = 1; //标记是否已经通过
if(length < templen)
{
length = templen; //更新步数
node = startnode; //保留编号
}
for(int i=0; i<num[startnode].size(); i++)
{
//递归进行深搜
if(!visited[num[startnode][i]])
DFS(num[startnode][i], templen+1);
}
}
同理:搜索这个也可以是BFS(广度优先搜索)。
代码理解:
1.数据结构
vector<int> num[20005]; //记录个点之间的关系
queue<int>p; //广搜必备
int len[20005]; //记录步数
bool visited[20005]; //标记是否走过
和深搜一样,还是用STl 向量vector数组来存储,各点之间的信息。
同时,广搜是需要用到队列的,即有queue<int>p; //广搜必备
用len[i]
来记录i点到起点的步数;
2.对于STL向量vector数组的初始化:
i与father两个点是有联系的,i与father相连,同时,father也与i相连。
for(int i=2; i<m+n; i++)
{
int father;
cin >> father;
num[i].push_back(father);
num[father].push_back(i);
}
3.重点:BFS(itn start)代码:
①.传参只有一个,就是起点的编号:第一次肯定是1,用全局变量来记录最长的树枝的编号。用node
来保存。
②.初始化队列,就是将起点直接入队就可以,标记走过。
③.重点操作就是:按着队列的顺序一个一个的遍历。遍历到一个点,就需要将这个点拿出来,继续扩展与之相连的点将满足条件的点入队。
并且每个扩展的点都要判断一下,走的步数len[k]
是不是大于目前保存的步数counts
,需要更新步数counts = len[k];
,并且记录这个点的编号node = k;
;
for(int j=0; j<num[i].size(); j++) //遍历与i相连的点
{
int k = num[i][j];
if(!visited[k]) //未走过
{
visited[k] = 1;
len[k] = len[i] + 1; //步数加1
if(len[k] > counts)
{
counts = len[k];
node = k;
}
p.push(k); //将与i相连,且满足条件的入队
}
}
三:代码
递归形式的深搜(DFS):
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n,m;
vector<int> num[20005];
int length, node;
int visited[20005] = {
0}; //标记是否走过
void DFS(int startnode, int templen)
{
visited[startnode] = 1; //标记已经通过
if(length < templen)
{
length = templen; //更新步数
node = startnode; //保留编号
}
for(int i=0; i<num[startnode].size(); i++)
{
//递归进行深搜
if(!visited[num[startnode][i]])
DFS(num[startnode][i], templen+1);
}
}
int main()
{
cin >> n >> m;
int father;
for(int i=2; i<=n+m; i++)
{
cin >> father;
//表示i与father建立联系
num[i].push_back(father);
num[father].push_back(i);
}
length = -1;
DFS(1,0); //起点是1,起始路程为0,目的是找到最远的那个点
length = -1;
memset(visited, 0, sizeof(visited));
DFS(node, 0); //从最远的这个点,再找最远的点就是所求的长度。
cout << length << endl;
return 0;
}
广搜:
//BFS
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
vector<int> num[20005]; //记录个点之间的关系
queue<int>p; //广搜必备
int len[20005]; //记录步数
bool visited[20005]; //标记是否走过
int node;
int BFS(int start)
{
memset(len, 0, sizeof(len));
memset(visited, 0, sizeof(visited));
int counts = -1;
//初始化队列
p.push(start);
visited[start] = 1;
//广搜
while(!p.empty())
{
int i = p.front();
p.pop();
for(int j=0; j<num[i].size(); j++) //遍历与i相连的点
{
int k = num[i][j];
if(!visited[k]) //未走过
{
visited[k] = 1;
len[k] = len[i] + 1; //步数加1
if(len[k] > counts)
{
counts = len[k];
node = k;
}
p.push(k); //将与i相连,且满足条件的入队
}
}
}
return counts;
}
int main()
{
int n, m;
cin >> n >> m;
for(int i=2; i<m+n; i++)
{
int father;
cin >> father;
num[i].push_back(father);
num[father].push_back(i);
}
BFS(1); //目的就是找最长的树枝的编号
int length = BFS(node); //目的是从找到的点找最长的直径
cout << length << endl;
return 0;
}