温故而知新
上次写的打印机选择在代码风格和一些细节方面太粗糙了,今天再写一遍。
【问题描述】
某单位信息网络结构呈树型结构,网络中节点可为交换机、计算机和打印机三种设备,计算机和打印机只能位于树的叶节点上。如要从一台计算机上打印文档,请为它选择最近(即经过交换机最少)的打印机。
在该网络结构中,根交换机编号为0,其它设备编号可为任意有效正整数,每个交换机有8个端口(编号0-7)。当存在多个满足条件的打印机时,选择按树前序遍历序排在前面的打印机。
【输入形式】
首先从标准输入中输入两个整数,第一个整数表示当前网络中设备数目,第二个整数表示需要打印文档的计算机编号。两整数间以一个空格分隔。假设设备总数目不会超过300。
然后从当前目录下的in.txt读入相应设备配置表,该表每一行构成一个设备的属性,格式如下:
<设备ID> <类型> <设备父节点ID> <端口号>
<设备ID>为一个非负整数,表示设备编号;<类型>分为:0表示交换机、1表示计算机、2表示打印机;<设备父结点ID>为相应结点父结点编号,为一个有效非负整数;<端口号>为相应设备在父结点交换机中所处的端口编号,分别为0-7。由于设备配置表是按设备加入网络时的次序编排的,因此,表中第一行一定为根交换机(其属性为0 0 -1 -1);其它每个设备结点一定在其父设备结点之后输入。每行中设备属性间由一个空格分隔,最后一个属性后有换行符。
【输出形式】
向控制台输出所选择的打印机编号,及所经过的交换机的编号,顺序是从需要打印文档的计算机开始,编号间以一个空格分隔。
【样例输入】
37 19
in.txt中的信息如下:
0 0 -1 -1
1 0 0 0
2 0 1 2
3 1 1 5
4 0 0 1
5 1 4 0
6 2 2 2
7 0 4 2
8 0 0 4
9 0 2 0
10 0 9 0
11 2 10 3
12 0 9 2
13 0 7 0
14 0 13 0
15 2 7 3
16 0 8 1
17 0 16 0
18 1 17 5
19 1 9 5
20 0 12 1
21 1 14 1
22 1 14 2
23 1 13 2
24 1 12 5
25 0 20 1
26 1 20 2
27 0 14 7
28 0 16 1
29 1 4 3
30 0 16 7
31 0 28 0
32 2 31 0
33 1 30 2
34 1 31 2
35 0 31 5
36 1 35 3
【样例输出】
11 9 10
【样例说明】
样例输入中37表示当前网络共有37台设备,19表示编号为19的计算机要打印文档。in.txt设备表中第一行0 0 -1 -1表示根节点交换机设备,其设备编号为0 、设备类型为0(交换机)、父结点设备编号-1表示无父设备、端口-1表示无接入端口;设备表第二行1 0 0 0表示设备编号为1 、设备类型为0(交换机)、父结点设备编号0(根交换机)、端口0表示接入父结点端口0;设备表中行5 1 4 0表示设备编号为5 、设备类型为1(计算机)、父结点设备编号4、端口0表示接入4号交换机端口0;设备表中行6 2 2 2表示设备编号为6 、设备类型为2(打印机)、父结点设备编号2、端口2表示接入2号交换机端口2。
样例输出11 9 10表示选择设备编号为11的打印机打印文档,打印需要经过9号和10号交换机(尽管6号和11号打印机离19号计算机距离相同,但11号打印机按树前序遍历时排在6号之前)。
【评分标准】
按题目要求实现相关功能,提交程序文件名为print.c。
这个题真的出的很有意义,考到了树里面常用的很多操作和算法(尤其是递归),大概步骤分为:
1·建树
2·寻找距离最短的打印机
3·输出路径
1·建树
由于文件给的是父节点的编号,所以需要先找到父节点。
typedef struct device_type
{
int ID;
int type;
int father;
int comID;
struct device_type *child[9];
} node;
node *tree;
node *find_node(node *p, int id)
{
if (p->ID == id)
return p;
for (int i = 0; i < 8; i++)
{
if (p->child[i] != NULL && find_node(p->child[i], id) != NULL)
return find_node(p->child[i], id);
}
return NULL;
}
node *make_tree()
{
FILE *src = fopen("in.txt", "r");
//初始化根节点
node *root = (node *)malloc(sizeof(node));
for (int i = 0; i < 9; i++)
root->child[i] = NULL;
int id, ty, par, com;
fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
root->ID = id;
root->type = ty;
root->father = par;
root->comID = com;
//录入子节点
int n;
scanf("%d", &n);
for (int i = 0; i < n - 1; i++)
{
//生成子节点
fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
node *p = (node *)malloc(sizeof(node));
for (int i = 0; i < 9; i++)
p->child[i] = NULL;
p->ID = id;
p->type = ty;
p->father = par;
p->comID = com;
//寻找父节点
node *q = find_node(root, par);
q->child[com] = p;
}
return root;
}
2·寻找最近的打印机
由于策略是在同距离的时候,前序遍历在前的优先级更高,所以我们先前序遍历,把打印机结点的指针放到一个数组里面存起来,然后遍历数组寻找距离最近的。
node *printer[100];
int pcnt = 0;
void get_printers(node *p)
{
if (p->type == 2)
printer[pcnt++] = p;
for (int i = 0; i < 8; i++)
{
if (p->child[i] != NULL)
get_printers(p->child[i]);
}
}
在求打印机和计算机的距离时,不必要使用递归(因为这样不方便距离的计算),所以直接使用循环:
int dist(node *p, node *q)
{
int d = 0;
node *t = p;
while (!is_ances(t, q))
{
d++;
t = find_node(tree, t->father);
} //寻找公共祖先
while (t != q)
{
for (int i = 0; i < 8; i++)
{
if (t->child[i] != NULL && is_ances(t->child[i], q))
{
t = t->child[i];
d++;
break;
}
}
}
return d;
}
is_ances函数比较简单:
int is_ances(node *p, node *q)
{
if (p == q)
return 1;
for (int i = 0; i < 8; i++)
{
if (p->child[i] != NULL && is_ances(p->child[i], q))
return 1;
}
return 0;
}
最后,有了这两个函数,只需要遍历即可:
node *find_printer(node *cpt)
{
node *p = printer[0];
int sdist = dist(p, cpt);
for (int i = 0; i < pcnt; i++)
{
if (dist(printer[i], cpt) < sdist)
{
sdist = dist(printer[i], cpt);
p = printer[i];
}
}
return p;
}
三·打印路径
其实打印路径和求距离基本上是一样的,只要把求距离函数的距离++改成打印结点就可以了,
注意到本题只要打印交换机结点,所以还需要判断一下是不是交换机
void show_path(node *p, node *q)
{
node *t = p;
while (!is_ances(t, q))
{
if (t->type == 0)
printf("%d ", t->ID);
t = find_node(tree, t->father);
} //寻找公共祖先
while (t != q)
{
for (int i = 0; i < 8; i++)
{
if (t->child[i] != NULL && is_ances(t->child[i], q))
{
if (t->type == 0)
printf("%d ", t->ID);
t = t->child[i];
break;
}
}
}
}
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct device_type
{
int ID;
int type;
int father;
int comID;
struct device_type *child[9];
} node;
node *tree;
node *find_node(node *p, int id)
{
if (p->ID == id)
return p;
for (int i = 0; i < 8; i++)
{
if (p->child[i] != NULL && find_node(p->child[i], id) != NULL)
return find_node(p->child[i], id);
}
return NULL;
}
node *make_tree()
{
FILE *src = fopen("in.txt", "r");
//初始化根节点
node *root = (node *)malloc(sizeof(node));
for (int i = 0; i < 9; i++)
root->child[i] = NULL;
int id, ty, par, com;
fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
root->ID = id;
root->type = ty;
root->father = par;
root->comID = com;
//录入子节点
int n;
scanf("%d", &n);
for (int i = 0; i < n - 1; i++)
{
//生成子节点
fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
node *p = (node *)malloc(sizeof(node));
for (int i = 0; i < 9; i++)
p->child[i] = NULL;
p->ID = id;
p->type = ty;
p->father = par;
p->comID = com;
//寻找父节点
node *q = find_node(root, par);
q->child[com] = p;
}
return root;
}
void pre_traverse(node *p)
{
printf("%d ", p->ID);
for (int i = 0; i < 8; i++)
{
if (p->child[i])
pre_traverse(p->child[i]);
}
}
node *printer[100];
int pcnt = 0;
void get_printers(node *p)
{
if (p->type == 2)
printer[pcnt++] = p;
for (int i = 0; i < 8; i++)
{
if (p->child[i] != NULL)
get_printers(p->child[i]);
}
}
int is_ances(node *p, node *q)
{
if (p == q)
return 1;
for (int i = 0; i < 8; i++)
{
if (p->child[i] != NULL && is_ances(p->child[i], q))
return 1;
}
return 0;
}
int dist(node *p, node *q)
{
int d = 0;
node *t = p;
while (!is_ances(t, q))
{
d++;
t = find_node(tree, t->father);
} //寻找公共祖先
while (t != q)
{
for (int i = 0; i < 8; i++)
{
if (t->child[i] != NULL && is_ances(t->child[i], q))
{
t = t->child[i];
d++;
break;
}
}
}
return d;
}
node *find_printer(node *cpt)
{
node *p = printer[0];
int sdist = dist(p, cpt);
for (int i = 0; i < pcnt; i++)
{
if (dist(printer[i], cpt) < sdist)
{
sdist = dist(printer[i], cpt);
p = printer[i];
}
}
return p;
}
void show_path(node *p, node *q)
{
node *t = p;
while (!is_ances(t, q))
{
if (t->type == 0)
printf("%d ", t->ID);
t = find_node(tree, t->father);
} //寻找公共祖先
while (t != q)
{
for (int i = 0; i < 8; i++)
{
if (t->child[i] != NULL && is_ances(t->child[i], q))
{
if (t->type == 0)
printf("%d ", t->ID);
t = t->child[i];
break;
}
}
}
}
int main()
{
tree = make_tree();
//pre_traverse(tree);
get_printers(tree);
//for(int i=0;i<pcnt;i++) printf("%d ",printer[i]->ID);
int computerID;
scanf("%d", &computerID);
node *computer = find_node(tree, computerID);
node *prt = find_printer(computer);
printf("%d ", prt->ID);
show_path(computer, prt);
}