//Kruskal, Union-Set
#include <iostream>
const int MAXN = 1010;
int N, M;
int pre[MAXN];
int rank[MAXN];
struct node
{
int x, y;
int length;
}net[MAXN * MAXN];
int cmp(const void* v1, const void* v2)
{
return ((node*)v1)->length > ((node*)v2)->length ? 1: -1;
}
void getInput()
{
scanf("%d%d", &N, &M);//get N, M
for(int i = 0; i < M; i++)
{
scanf("%d%d%d", &net[i].x, &net[i].y, &net[i].length);
}
}
int find(int x)
{
if(x != pre[x])
pre[x] = find(pre[x]);
return pre[x];
}
void unionOp(int x, int y)
{
if(rank[x] < rank[y])
{
pre[x] = pre[y];
}
else if(rank[x] > rank[y])
{
pre[y] = pre[x];
}
else
{
pre[y] = pre[x];
rank[x] = rank[x] + 1;
}
}
node res[MAXN];
int main()
{
getInput();
qsort(net, M, sizeof(node), cmp);
for(int i = 1; i < MAXN; i++)
{
pre[i] = i;
rank[i] = 0;
}
int sum = 0;
int max = 0;
int cnt = 0;
for(int i = 0; i < M; i++)
{
int x = find(net[i].x);
int y = find(net[i].y);
if(x != y)
{
unionOp(x, y);
res[cnt].x = net[i].x;
res[cnt].y = net[i].y;
cnt++;
sum += net[i].length;
if(net[i].length > max)
max = net[i].length;
}
}
printf("%d\n", max);
printf("%d\n", cnt);
for(int i = 0; i < cnt; i++)
{
printf("%d %d\n", res[i].x, res[i].y);
}
return 0;
}