CCF-201403-4(无线网络)

一:问题
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;
}

猜你喜欢

转载自blog.csdn.net/pfl_327/article/details/104803098