//这是一道典型的水题,其实就是一道非常基础的图论入门题
//直接去用prim算法,就是个prim算法实现的最小生成树
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
static int max=Integer.MAX_VALUE;
public static void prim(int [][]map,int n) {
//存到集合的最小权值,相当于数据结构prim算法测试表的每一行的表示权值的那一行数据
int []lowcost=new int[n];
int i,j,min,minid,sum=0;
for(i=1;i<n;i++) {
//一开始初始为直接与0这个节点相连的节点到0的权值
//符合数据结构prim算法测试表的第一行
lowcost[i]=map[0][i];
}
//一共需要加n-1个节点到这个集合中,循环次数
for(i=1;i<n;i++) {
min=max;
minid=0;
for(j=1;j<n;j++) {
//如果这个节点没有被加过lowcost[j]!=0,因为加过之后会被赋值为0
//或者两个节点之间没有路时,也被默认为0;这种情况此时也巧好被排除
//在没有被加过的节点中找到此时离集合最近的那一个节点,
//在老师讲的那个表中就是在此时的每一行中挑选出最小的权值的节点
if(lowcost[j]!=0&&lowcost[j]<min) {
min=lowcost[j];
//minid用来记录此时的节点的序号,跳出循环时记录的就是下一个要加入集合的结点的序号
minid=j;
}
}
//如果minid==0,说明在上一个,离这里最近的那一个for循环没有使初值为0的minid的值
//发生改变,就是没有if成立的条件,说明此时的lowcost数组中每一个元素都等于0了;说明都被加过了
//因为min初始值是max,因此肯定是lowcost数组的元素都等于0了;
//这时就可以直接跳出这个大的for循环了
if(minid==0) {
return;
}
//节点被加过之后,让lowcost[这个节点的id]=0;
//这样就可以不影响下一个要选出来的节点了
lowcost[minid]=0;
//最小生成树的路径和
sum+=min;
//更新lowcost数组,因为新加入了一个节点,这个节点可能有以下两种情况
//1,与其他原来与集合中元素都没有路的节点有路,那么就要更新此时的lowcost数组
//2,与其他原来与集合中元素有路的节点也有路,那么就要比较是原来的lowcost[j]小,还是
//此时新加入的节点到这个节点的路map[minid][j]小,然后更新lowcost[j]为较小的数
for(j=1;j<n;j++) {
if(lowcost[j]!=0&&lowcost[j]>map[minid][j]) {
lowcost[j]=map[minid][j];
}
}
}
System.out.println(sum);
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int [][]map=new int[n][n];
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
map[i][j]=in.nextInt();
}
}
prim(map,n);
}
}