题目:
构建一棵树(Completing a Tree)
Given: A positive integer n (n≤1000) and an adjacency list corresponding to a graph on n nodes that contains no cycles.
所给:一个不大于1000的正整数n以及一个邻接表,对应一个有n个结点且无环的图。
Return: The minimum number of edges that can be added to the graph to produce a tree.
需得:添加到图中从而构成树的边的最小数目。
测试数据
10
1 2
2 8
4 10
5 9
6 10
7 9
测试输出
3
生物学背景
进化树是进化生物学中用来表示物种进化关系的图表。从进化树中我们可以读出物种间的进化关系和距离。
数学背景
所谓无向图就是边没有方向,相连两个点可以互相抵达的图。树则是不含环的无向图,结构与自然界中的相对应,如下所示:
无向图顶点的边数称为度(degree),度为1的结点为叶子结点,度大于1的结点为内节点。
邻接表是数组与链表相结合的存储方法。本题中所给的邻接表每行给出的两个数字代表两个结点由边相连。
思路
由给出的示例数据可以画出下图:
从绘图结果很容易看出,这十个结点形成了四“堆”:
只需要添加三条边就可以变成一棵树。
示例所给的数据比较简单,但对复杂数据,原理是相同的,请详见代码注释。
代码
f =open('input.txt','r')
lines = f.readlines()
f.close()
num = lines[0]#存储结点的数目edges = lines[1:]#存储边的关系i =1nodes = []whilei <=int(num):
nodes.append(str(i))#用一个列表保存所有节点i +=1fedge = edges[0].replace('\n','')#把邻接表第一行取出来,作为第一“堆”的基础fedge = fedge.split(' ')
nodes.remove(fedge[0])
nodes.remove(fedge[1])
tree = [fedge]# tree用来存储结点通过邻接表存储的关系形成的“堆”flag =1# flag存储现在tree里有几“堆”i =1whilei
edge = edge.split(' ')ifedge[0]innodes:
nodes.remove(edge[0])#每取出一个边,就把形成边的结点从结点列表中删去ifedge[1]innodes:
nodes.remove(edge[1])
j =0tag =0note = []whilej < flag:# j用来遍历各“堆”ifedge[0]intree[j]:#如果这条边的一个结点已经在其中一个“堆”里tree[j].append(edge[1])#把这个边加入这一“堆”tag +=1note.append(j)# note存储这个结点在哪个“堆”j +=1elifedge[1]intree[j]:
tree[j].append(edge[0])
tag +=1note.append(j)
j +=1else:#如果不在这一“堆”里,j加一,以查找下一“堆”j +=1iftag ==0:#如果在之前存在的“堆”里都没有这条边tree.append(edge)
flag +=1#添加一个新的“堆”iftag ==2:#如果两个结点各在两“堆”中,说明这两个“堆”可以合并flag -=1tree[note[0]].extend(tree[note[1]])
tree.remove(tree[note[1]])#合并两“堆”并删去其中一个tree[note[0]].remove(edge[0])
tree[note[0]].remove(edge[1])#删去由合并导致的重复结点i +=1print(tree)
lennodes =len(nodes)#记录现在结点列表中还有几个结点,每个都相当于一个单独的“堆”sum = (flag -1) + lennodes#“堆”的数量减去一即为需要添加的边的数量print(sum)