#include<stdio.h>
#include<string.h>
const int INF = 0x3f3f3f;
const int N = 105;
/*
简述prim过程
1.初始化最小生成树(最开始只有一个点,这个点可以是任意一个点) 
2.维护最小生成树,每次选取离原有生成树最近的点,并加入最小生成树,
	然后用这个点更新其他点到最小生成树的距离(类似于松弛) 
*/ 
int n,m;
int data[N][N];


int prim();
int main()
{
	scanf("%d %d", &n, &m);        //点的数目和边得数目



	for(int i =0; i <=n;i++)            //初始化为一个极大值。(若两点之间没有连通,则用极大值替代)
		for(int j = 0; j <=n; j++)
			data[i][j] = INF;
			
	for(int i =0; i < m; i++)        //prim用邻接矩阵建图(所以适用于稠密图)
	{
		int x,y,w;
		scanf("%d %d %d",&x, &y,&w);
		data[x][y] = data[y][x] = w;
	}
	
	int result = prim();
	printf("%d\n",result);
	
	
	
	
	return 0;
}
int prim()
{
	int dis[N],sum = 0;
	int mark[N] = {0};
	int edge[N];        //edge[i] = j表示在生成树中,j的父亲是i;
	mark[1] = 1;
	for(int i = 1; i <=  n; i++)
	{
		dis[i] = data[1][i];
		edge[i] = 1;    //初始化edge,刚开始生成树中只有点1,所以其他的点都指向点1
	}
	
	
	for(int i =1; i < n; i++)
	{
		int min_val = INF;
		int min_id;
		
		for(int j = 1; j <=n; j++)
		{
			if(mark[j] == 0 && min_val > dis[j])
			{
				min_val = dis[j];
				min_id = j;
			}
		}
		sum += min_val;
		mark[min_id] = 1;
		
		if(data[edge[min_id]][min_id] > 0)
		printf("%d %d\n",edge[min_id],min_id);
		
		for(int k =1; k <= n; k++)
		{
			if(mark[k] == 0 && dis[k] > data[min_id][k])
			{
				dis[k] = data[min_id][k];
				edge[k] = min_id;        //更新min_id的儿子;
			}
		}
		
	}
	
	return sum;
}