题目链接:码题集OJ-洞穴 (matiji.net)
怪诞小镇的森林里有一个奇怪的洞穴群。
这个洞穴群包括 n 个洞穴(从 1 到 n 编号)。
每个洞穴中有若干岔路通向其他的洞穴,每条岔路都有一个长度。
日志3上记录了这个洞穴群的一个特点:任意两个洞穴间仅存在一条路径。
此外,日志的主人还在日志3上写下了任意两个洞穴间的距离。
现在小度想要知道这个洞穴群中,哪两个洞穴间存在岔路。
格式
输入格式:
多组样例输入,第一行一个整数 T 表示数据组数。
每组数据包括 n+1 行。
第一行一个数字 n 表示洞穴的个数
之后 n 行每行 n 个整数,第 x 行的第 y 个整数 disx,_y_ 表示洞穴 x 和洞穴 y 间的距离。
(T≤10,1≤n≤100,)
输出格式:
对于每个样例,首先输出一行一个整数 k,表示洞穴群中存在 k 条岔路。
之后 k 行输出 k 个二元组 (x,y),表示洞穴 x 和洞穴 y 间有一条直接连通的岔路。
请按照(x,y) 递增的顺序输出:即 x 不同时先输出 x 较小的(x,y);x 相同时先输出 y 较小的 (x,y)。
样例 1
输入:
1
5
0 1 2 3 3
1 0 1 2 2
2 1 0 3 3
3 2 3 0 4
3 2 3 4 0
复制
输出:
4
1 2
2 3
2 4
2 5
满足输入距离的洞穴群结构如图。
题解:
思路:如果题目看不懂,可以把邻接矩阵列出来,如图,将点代入分析发现和Floyd最短路径有些许相似,Floyd算法的关键是寻找加入点后的最短路径:
核心代码:graph[i][j] = min(graph[i][k] + graph[k][j],graph[i][j])
本题的关键也是题目要求是加入点后判断任意两点是否直接连通,题目中任意两洞穴只有一条路径是关键,这也就意味着C->D和C->B->D必须二选一!若C->D==C->B->D,那么路径一定是不符合要求的C->B->D,因为若C->D也成立那么就违反了任意两洞穴只有一条路径。有同学又会问了,会不会是只存在C->D而不是C->B->D呢?答案是否定的,因为看上面的表格会发现C->B->D是确实存在的!因此C->D由于经过了B而不符合题目的直接连通,我们使用flag=False进行短暂的记录。核心代码也就在这里体现,代码如下:
if dis[i][j]==dis[i][k]+dis[k][j]: flag=False break
具体完整可运行代码为:
def Floyd():
T = int(input())
for t in range(T):
res = []
n = int(input()) # 洞穴的个数
dis = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
row=list(map(int,input().split()))
for j in range(n):
dis[i][j]=row[j]
for i in range(n):
for j in range(i+1,n):
flag=True
for k in range(n):
if k==i or k==j: #如果不加的话,k=1,计算[1][2]时,[1][2]=[1][1]+[1][2]=[1][2],直接执行flag=False
#简而言之,不加中转点是否等于自身的判断,会导致把自身也当成中转节点
continue
if dis[i][j]==dis[i][k]+dis[k][j]:
flag=False
break
if flag:
res.append((i+1,j+1))
res.sort()
print(len(res)) # 输出直接连通的岔路的个数
for item in res:
print(item[0], item[1]) # 输出结果
Floyd()
这里仍然有几个需要注意的点:
- python一定要看缩进,写到最后的排序写道循环外面去了,害我调了半天。。。
- append添加元组能过,添加列表(res.append([i+1,j+1]) )不能过,但是输出结果都一样。。。又害我调了半天
- j的遍历循环要从i+1,n 因为要避免重复计算,例如计算了[1][2]就不用计算[2][1]了,相当于无向图的Floyd
- if k==i or k==j:
continue
#如果不加的话,k=1,计算[1][2]时,[1][2]=[1][1]+[1][2]=[1][2],直接执行flag=False
#任何判断都会经历到k==i or k==j:这一步所以最终结果输出0
#简而言之,不加中转点是否等于自身的判断,会导致把自身也当成中转节点
第一次在CSDN发布,如有不足和建议请给我留言。