2021-02-20

北航机试 2019 网络打印机

一个网络中有三种设备:交换机、电脑和打印机,网络成树状,电脑和打印机只在叶子节点上。交换机有4个端口。
首先输入一个数N表示设备的个数。
然后按照id,类型,父节点id,端口号依次输入每台设备
最后输入一个id(电脑),找距离这台电脑最近的打印机,如果有多个则按照前序遍历的顺序找到打印机
输出这台打印机的编号和从电脑到这台打印机的路径。
输入:
当前网络中设备数m
<设备ID><设备类型><设备父节点ID><端口号>(类型:0表示交换机,1表示计算机,2表示打印机)
输出:
最近打印机的ID,以及从电脑到打印机的路径。

题目要做什么:一棵树,电脑和打印机是叶子节点,给你一个电脑叶子节点,要找到离它最近的另一个打印机叶子结点。
注意点:

  1. 树是4叉树,叉的数量就是交换机的端口数量。每行的输入里面的端口号,是指本节点插在父节点的那个端口号上,这个条件的用处就是多叉树建树用的,表明孩子节点是有先后之分的,并且有些孩子可能不存在。(想一想二叉树和度为二的树的区别)。
  2. 打印机和电脑的距离指的是什么?指的是简单路径的长度,可以理解为图论的最短路径。
  3. 如果有多个则按照前序遍历的顺序找到打印机,这什么意思?就是说,如果存在多个打印机满足距离最小,那么输出前序遍历最先找到那个,也就是前序遍历序列中排最前面那个。可以先对树进行前序遍历,每个节点打上标号,然后遇到距离相等的打印机时,用标号来决定。

我的思路是先前序遍历打标号,然后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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值