// 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的长度最大值作为输出.