package 图论算法;
//参考博客
//javascript:void(0)
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class prim {
static int max=Integer.MAX_VALUE;
public static void prim(int[][]map,int n) {
//设置序号从0开始的节点
char[]c=new char[] {'a','b','c','d','e','f','g','h','i'};
//存到集合的最小权值,相当于数据结构prim算法测试表的每一行的表示权值的那一行数据
int []lowcost=new int [n];
//存前驱结点,也就是数据结构的那个表中的每一行的第一层的数据,就是每一行第二层这一个权值是由哪一个节点到这个节点的
int []mid=new int[n];
//用来存储加入结点的顺序
List<Character>list=new ArrayList<Character>();
int i,j,min,minid,sum=0;
for(i=1;i<n;i++) {
//一开始初始为直接与0这个节点相连的节点到0的权值
//符合数据结构prim算法测试表的第一行
lowcost[i]=map[0][i];
//mid数组一开始被初始化为0,因为数据结构那一个表中第一行就是序号为0的那个结点到其他各点的距离
//所以此时的前驱就是序号0结点
mid[i]=0;
}
list.add(c[0]);
//一共需要加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;
}
//将此时的这个要加入集合的结点加入链表
list.add(c[minid]);
//节点被加过之后,让lowcost[这个节点的id]=0;
//这样就可以不影响下一个要选出来的节点了
lowcost[minid]=0;
//最小生成树的路径和
sum+=min;
System.out.println(c[mid[minid]]+"到"+c[minid]+" 权值:"+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];
//每一次更新了lowcost数组,那么此时,这个节点的前驱就应该是此时的新加入的那个结点的minid
mid[j]=minid;
}
}
}
System.out.println("sum:"+sum);
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
//节点数
int n=in.nextInt();
//边数
int m=in.nextInt();

int [][]map=new int[n][n];
//初始化邻接表
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
//结点自己到自己权值为0
if(i==j) {
map[i][j]=0;
}
//否则初始为最大值
else {
map[i][j]=max;
}
}
}
//根据输入的两结点之间的权值,填入邻接表
for(int i=0;i<m;i++) {
int a=in.nextInt();
int b=in.nextInt();
int v=in.nextInt();
map[a][b]=v;
map[b][a]=v;
}
prim(map,n);

}
}
//9
//14
//0 1 10
//0 5 11
//1 2 18
//1 6 16
//1 8 12
//2 3 22
//2 8 8
//3 4 20
//3 7 16
//3 8 21
//4 5 26
//4 7 7
//5 6 17
//6 7 19
//a到b 权值:10
//a到f 权值:11
//b到i 权值:12
//b到g 权值:16
//b到c 权值:18
//g到h 权值:19
//c到d 权值:22
//d到e 权值:20
//sum:128


//8
//12
//0 1 21
//0 2 18
//0 3 94
//1 3 12
//2 3 36
//2 4 89
//4 6 59
//6 7 36
//5 7 54
//3 5 57
//2 7 69
//3 7 98
//a到c 权值:18
//a到b 权值:21
//b到d 权值:12
//d到f 权值:57
//f到h 权值:54
//h到g 权值:36
//g到e 权值:59
//sum:257


//8
//12
//0 1 70
//0 2 70
//1 3 94
//2 3 30
//0 3 19
//2 4 79
//4 6 24
//6 7 25
//5 7 88
//3 5 11
//2 7 51
//3 7 84
//a到d 权值:19
//d到f 权值:11
//d到c 权值:30
//c到h 权值:51
//h到g 权值:25
//g到e 权值:24
//a到b 权值:70
//sum:230