class MergeSet:
def __init__(self):
self.m = {}
self.__root_cnt = 0
def getRoot(self, node):
root = node
buf = []
while self.m[root] != root:
buf.append(root)
root = self.m[root]
for node in buf:
self.m[node] = root
return root
def merge(self, a, b):
for node in [a, b]:
if node not in self.m:
self.m[node] = node
self.__root_cnt += 1
root1 = self.getRoot(a)
root2 = self.getRoot(b)
if root1 != root2:
self.m[root1] = root2
self.__root_cnt -= 1
def isInSameSet(self, a, b):
if a == b:
return True
for node in [a, b]:
if node not in self.m:
return False
return self.getRoot(a) == self.getRoot(b)
def getRootNum(self):
return self.__root_cnt
def getClusters(self):
rec = {}
for node in self.m:
root = self.getRoot(node)
if root not in rec:
rec[root] = []
rec[root].append(node)
return [nodes for nodes in rec.values()]
# 克鲁斯卡尔算法求解最小生成树输入为边列表,每条边表示为(起点,终点,权重)
# 返回最小生成树权重总和,以及最小生成树边列表, 生成不了最小生成树返回None
def getMinSpanTreeKruscal(edges):
if len(edges) == 0:
return [None, None]
edge_list = [(w, a, b) for a, b, w in edges]
edge_list.sort()
merge_set = MergeSet()
span_tree = []
node_set = set()
for a, b, _ in edges:
node_set.add(a)
node_set.add(b)
sum = 0
for w, a, b in edge_list:
if merge_set.isInSameSet(a, b):
continue
merge_set.merge(a, b)
span_tree.append((a, b))
sum += w
if len(span_tree) == len(node_set) - 1:
break
return [sum, span_tree] if len(span_tree) == len(node_set) - 1 else [None, None]
edges = []
n = int(input())
for i in range(n):
w = int(input())
edges.append((n, i, w))
for i in range(n):
line = list(map(int, input().split()))
for j in range(i+1, n):
edges.append((i, j, line[j]))
print(getMinSpanTreeKruscal(edges)[0])