📅  最后修改于: 2023-12-03 14:50:46.002000             🧑  作者: Mango
这是关于国际空间研究组织(ISRO)在2007年组织的CS(Civil Service)考试的第71题。CS考试是ISRO用来招聘程序员和其他职位的考试之一。
给定一个带权的、无向的联通图。权重表示每条边的长度。从任意一个顶点出发,到达另外一个顶点的路径称为 "通路"。通路的长度为路径上所有边的权重之和。最小生成树是一张图的生成树中,边权之和最小的生成树。请问,最小生成树的边权之和可能等于哪些值?
第一行包含一个整数 $T$,表示测试数据的组数。
对于每组测试数据:
第一行包含两个整数 $N$ 和 $M$,表示图的节点数和边数。
接下来 $M$ 行,每行三个整数 $x$,$y$ 和 $w$,表示节点 $x$ 和节点 $y$ 之间有一条无向边,边权为 $w$。
对于每组测试数据,输出一行,包含所有可能的最小生成树的边权之和,按从小到大排序,并用空格隔开。如果不存在最小生成树,则在一行中输出 "NO"。
2
3 3
1 2 1
2 3 2
1 3 3
3 3
1 2 1
2 3 2
3 1 3
3 4
NO
这是一道关于最小生成树的寻找的问题。最小生成树可以用Kruskal算法或Prim算法寻找。如果最小生成树存在,则其边权之和必定唯一,因此可以用Kruskal算法或Prim算法求出最小生成树,并输出其边权之和。如果最小生成树不存在,则无法求出其边权之和。
请注意,如果有一条边的权重相同则可能存在多个边权值相同的最小生成树。
要求出所有可能的最小生成树的边权值之和,需要按照如下步骤:
由于此题要求输出所有可能的最小生成树的边权之和,而不是求最小生成树的边权之和,因此最后要将结果排序,以便按升序输出。
import heapq
def prim(graph):
nodes = set(graph.keys()) # 节点集合
visited = set() # 已访问的节点集合
heap = [] # 用于保存边的最小堆
root = None # 起点
mst = [] # 最小生成树的边集合
for node in nodes:
root = node # 任意选一个点作为起点
break
visited.add(root)
# 将从起点开始的所有边加入最小堆
for neighbor, weight in graph[root]:
heapq.heappush(heap, (weight, root, neighbor))
while heap and nodes != visited:
weight, frm, to = heapq.heappop(heap)
if to not in visited:
visited.add(to)
mst.append((frm, to, weight))
# 将新添加的节点的邻接边加入最小堆
for neighbor, weight in graph[to]:
heapq.heappush(heap, (weight, to, neighbor))
# 如果最小生成树包含所有节点,则返回边集合
if nodes == set([x[0] for x in mst] + [x[1] for x in mst]):
return mst
# 如果最小生成树不包含所有节点,则返回NO
return "NO"
def kruskal(edges, nodes):
sorted_edges = sorted(edges, key=lambda x: x[2]) # 按权重排序
parent = {node: node for node in nodes} # 初始化并查集
mst = [] # 最小生成树的边集合
for edge in sorted_edges:
frm, to, weight = edge
group1 = parent[frm] # 查找edge的两个顶点所在的组
group2 = parent[to]
if group1 != group2: # 如果不在同一组,则将edge加入最小生成树
mst.append(edge)
# 将group1和group2并为一组
for key, value in parent.items():
if value == group2:
parent[key] = group1
# 如果最小生成树包含所有节点,则返回边集合
if set(parent.values()) == set([parent[node] for node in nodes]):
return mst
# 如果最小生成树不包含所有节点,则返回NO
return "NO"
for _ in range(int(input())):
n, m = map(int, input().split())
graph = {}
for i in range(m):
x, y, w = map(int, input().split())
if x not in graph:
graph[x] = []
if y not in graph:
graph[y] = []
graph[x].append((y, w))
graph[y].append((x, w))
mst1 = prim(graph)
if mst1 == "NO":
print("NO")
continue
edges = [(min(u, v), max(u, v), w) for u, v, w in mst1] # 生成Kruskal算法需要的边集合
nodes = set(graph.keys()) # 节点集合
mst2 = kruskal(edges, nodes)
if mst2 == "NO":
print("NO")
continue
result = set([sum([x[2] for x in mst1])]) # 添加最小生成树的边权之和
for edge1 in mst1:
for edge2 in mst2:
if set(edge1) & set(edge2): # 如果两个最小生成树有交点,则跳过
continue
graph = {}
for i in range(m):
x, y, w = map(int, input().split())
if x not in graph:
graph[x] = []
if y not in graph:
graph[y] = []
graph[x].append((y, w))
graph[y].append((x, w))
graph[edge1[0]].remove((edge1[1], edge1[2])) # 删除edge1
graph[edge1[1]].remove((edge1[0], edge1[2]))
graph[edge2[0]].remove((edge2[1], edge2[2])) # 删除edge2
graph[edge2[1]].remove((edge2[0], edge2[2]))
new_mst = prim(graph) # 求新的最小生成树
if new_mst != "NO":
result.add(sum([x[2] for x in new_mst]))
result = sorted(list(result))
if result:
print(" ".join([str(x) for x in result]))
else:
print("NO")
这段代码使用Prim算法求出一个最小生成树$T$,然后使用Kruskal算法求出另一个最小生成树$T'$。接下来,我们对于$T$中的每一条边$e$,删除它,并再次用Prim算法求出新的最小生成树$T''$。我们比较$T''$和$T'$的边权之和$sum(T'')$和$sum(T')$,如果它们相等,则$T''$也是一个最小生成树,将其边权之和加入结果集合中。最后,按升序输出结果集合中的元素。如果结果集合为空,则说明没有最小生成树,输出"NO"。