// 596K    141MS   G++
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;


struct Connetcions {
    int begin;
    int end;
    int length;
    char left;
};

int cmp(const void * a, const void * b) {
    return (*((Connetcions *)a)).length - (*((Connetcions *)b)).length;
}

struct union_find_member {
    int setId; // begin from 1, 0 means not filled yet.
    // int Prev;
    // int prevLength;
};

typedef struct union_find_member union_find_member;
typedef struct Connetcions Connetcions;

union_find_member UF_set[1010];
Connetcions connections[15010];
int leftConnectionsNum;

int hubNum;
int connectionNum;
int maxlength = 0;

int getSetId(int nodeId) {

    if (UF_set[nodeId].setId == 0) {
        return 0;
    }

    int curSetId = UF_set[nodeId].setId;
    while(UF_set[curSetId].setId != curSetId) {
        curSetId = UF_set[curSetId].setId;
    }
    UF_set[nodeId].setId = curSetId;
    return curSetId;
}

char UF_fill(int beginId, int endId, int connectionlength) {
    int beginSetId = getSetId(beginId);
    int endSetId = getSetId(endId);
    // printf("%d -> %d, %d -> %d\n", beginId, beginSetId, endId, endSetId);

    // if begin and end all no filled before
    if (beginSetId == 0 && endSetId == 0) {

        UF_set[beginId].setId = beginId;
        UF_set[endId].setId = beginId;

    } else if (!beginSetId && endSetId) {

        UF_set[beginId].setId = endSetId;

    } else if (beginSetId && !endSetId) {

        UF_set[endId].setId = beginSetId;
        // printf("A %d %d\n", beginSetId, UF_set[endId].setId);
    } else if (beginSetId && endSetId) {
        if (beginSetId != endSetId) {
            UF_set[endSetId].setId = beginSetId;
            UF_set[endId].setId = beginSetId;
        } else {
            // has all filled, no need this cable, and becuase sorted before,
            // so it must >= current cable.
            return 0;
        }
    }
    return 1;
}



int main() {
    while(scanf("%d %d", &hubNum, &connectionNum) != EOF) {
        maxlength = 0;
        memset(UF_set, 0, sizeof(UF_set));
        memset(connections, 0, sizeof(connections));
        leftConnectionsNum = 0;
        for (int i = 1; i <= connectionNum; i++) {
            int beginId;
            int endId;
            int connectionlength;
            scanf("%d %d %d", &beginId, &endId, &connectionlength);
            connections[i-1].begin = beginId;
            connections[i-1].end = endId;
            connections[i-1].length = connectionlength;
        }
        qsort(connections, connectionNum, sizeof(Connetcions), cmp);
        for (int i = 0; i < connectionNum; i++) {
            // printf("%d %d %d\n", connections[i].begin,
                    // connections[i].end, connections[i].length);
            if (UF_fill(connections[i].begin, connections[i].end,
                            connections[i].length)) {
                connections[i].left = 1;
                maxlength = connections[i].length > maxlength ?
                            connections[i].length: maxlength;
                leftConnectionsNum++;
            }
        }

        printf("%d\n%d\n", maxlength, leftConnectionsNum);
        for (int i = 0; i < connectionNum; i++) {
            if (connections[i].left) {
                printf("%d %d\n", connections[i].begin, connections[i].end);
            }
        }
    }
}



算是道并查集的简单题,不过我一开始被搞晕了,纠结于最短路径的问题,并且,一开始,还想着把hub之前的连接关系直接集成到并查集里做,但是后面才发现,简直是自坑,因为并查集这种数据结构不适合来存储这种关系(其实真存也行的), 就另开一个数组来储存最后可以被留下来的cable连接,这道题其实也可以用最小生成树做的。

用并查集做,有个很关键的预处理步骤,那就是把所有输入的cable 按照 长度来进行从小达大的排序,这样才能达到题目的目的,尽量的保留长度最短,并且尽量去掉多余的cable,那么接下来就是并查集的标准操作了,并查集在这里的作用,就是检查输入的两个hub是否已经能被之前的cabl连通了,只需判断这些hub是否属于并查集的同一个集即可(因为这里的cable是双向的), 对于输入的一个cable,有两个hub, A 和B,那么,在将其加入到并查集中时,会有这些case:

case1: A 和 B 之前都没有被添加过, 那么直接将A 和B做为一个集, A作为B的集合Id.

case2: A添加过,B没有添加过,那么直接将B加入到A的集合中,B的集合id 是 A的集合Id.

case3: 和case2相反,A B倒换.

case4: A和B之前都已经被添加过了,又会有两个子case:

         sub-case1: A 和 B的集合Id一样,说明之前加入的cable已经能够将A 与 B连通,并且因为之前已经排过序,因此之前的cable各自的长度一定<=当前这段cable,

                           那么,按照题意,这段cable可以被去掉,直接pass,这里显示出了排序的重要性,否则,可能虽然A B都连通了,但是连接他们的cable可能比这次的要长, 注意题目   没有要求从长度最短,而是要求一段cable最短。

         sub-case2: A 和 B不一个集合,那么就将B的set的top元素的setId设为A的setId, 即将此B所属集合并入到A的集合。

在上面的处理过程中,还要收集被留下来的cable的长度最大值作为输出.