北航机试 2019 网络打印机
一个网络中有三种设备:交换机、电脑和打印机,网络成树状,电脑和打印机只在叶子节点上。交换机有4个端口。
首先输入一个数N表示设备的个数。
然后按照id,类型,父节点id,端口号依次输入每台设备
最后输入一个id(电脑),找距离这台电脑最近的打印机,如果有多个则按照前序遍历的顺序找到打印机
输出这台打印机的编号和从电脑到这台打印机的路径。
输入:
当前网络中设备数m
<设备ID><设备类型><设备父节点ID><端口号>(类型:0表示交换机,1表示计算机,2表示打印机)
输出:
最近打印机的ID,以及从电脑到打印机的路径。
题目要做什么:一棵树,电脑和打印机是叶子节点,给你一个电脑叶子节点,要找到离它最近的另一个打印机叶子结点。
注意点:
- 树是4叉树,叉的数量就是交换机的端口数量。每行的输入里面的端口号,是指本节点插在父节点的那个端口号上,这个条件的用处就是多叉树建树用的,表明孩子节点是有先后之分的,并且有些孩子可能不存在。(想一想二叉树和度为二的树的区别)。
- 打印机和电脑的距离指的是什么?指的是简单路径的长度,可以理解为图论的最短路径。
- 如果有多个则按照前序遍历的顺序找到打印机,这什么意思?就是说,如果存在多个打印机满足距离最小,那么输出前序遍历最先找到那个,也就是前序遍历序列中排最前面那个。可以先对树进行前序遍历,每个节点打上标号,然后遇到距离相等的打印机时,用标号来决定。
我的思路是先前序遍历打标号,然后DFS暴力搜索打印机,更新最短路径和最小标号,保存当前最优路径。复杂度不是最优,但是容易实现。DFS具体来说,把树看作无向图,注意需要vis数组,因为无向图本质上是有环的。用BFS也是可以的。
测试数据请到慕弋云子的机试指南那篇文章找。
#include <cstdio>
#include <vector>
#include <cassert>
#include <algorithm>
using namespace std;
/*
先前序遍历树,为每个节点标序号,然后DFS找最短路(其实就是简单路径),
按照路径最短和前序序号最小的原则更新,同时记录路径。
复杂度是遍历图,即O(V+E)。
输出打印机的编号和电脑到打印机的路径。
*/
// (类型:0表示交换机,1表示计算机,2表示打印机)
enum {
SW,COM,PRI,
};
#define MAXP 10 // 端口号
struct Node {
int type;
int ord; // 前序序号。
int fa;
int child[MAXP];
Node() {
fa=-1;
fill(child,child+MAXP,-1);
}
};
#define MAXN 505
Node node[MAXN];
bool vis[MAXN];
int N; // 节点编号1--N,0表示没有父节点。
void PreOrder(int root, int& index) {
if (root != -1) {
node[root].ord=index++;
for (int i=0;i<MAXP;++i) {
PreOrder(node[root].child[i], index);
}
}
}
vector<int> Path, tempPath;
// 从源点出发找到每一台打印机。
void DFS(int v) {
vis[v]=true;
tempPath.push_back(v);
if (node[v].type == PRI) {
// 找到一台打印机。
if (Path.empty() || (tempPath.size() < Path.size()) ||
(tempPath.size()==Path.size() && node[v].ord < node[Path.back()].ord)) {
Path=tempPath; // 注意更新条件。
}
} else {
// 因为是无向图,从父节点和孩子节点出发遍历。
int u=node[v].fa;
if (u != -1 && !vis[u]) {
DFS(u);
}
for (int i=0;i<MAXP;++i) {
int u=node[v].child[i];
if (u != -1 && !vis[u]) {
DFS(u);
}
}
}
vis[v]=false;
tempPath.pop_back();
}
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int Root;
int S; // 源点。
int main(int argc, char** argv) {
scanf("%d",&N);
fill(node, node+MAXN, Node());
fill(vis, vis+MAXN, false);
for (int i=0;i<N;++i) {
int id,type,fa,port;
// 注意,端口号是用来给孩子节点排序的。
scanf("%d%d%d%d",&id,&type,&fa,&port);
node[id].type=type;
if (!fa) {
Root=id;
} else {
node[id].fa=fa;
node[fa].child[port]=id;
}
}
scanf("%d",&S);
int index=0;
PreOrder(Root, index);
DFS(S);
int v=Path.back(); // 打印机的编号。
assert(node[v].type==PRI);
printf("%d\n", v);
for (int i=0;i<Path.size();++i) {
printf("%d%s",Path[i],i==Path.size()-1?"\n":" ");
}
return 0;
}