#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct edge { int begin; int end; int weight; }; const int maxn = 110; int father[maxn]; edge e[maxn * maxn]; int map[maxn][maxn]; int n, m; int mst_edge[maxn]; int find(int x) { if (x == father[x]) { return x; } father[x] = find(father[x]); return father[x]; } int kruscal(int count) { //使用kruscal算法来生成最小生成树并计算带权路径和 int i; int sum = 0; //用sum来记录最小s生成树的边权和 memset(mst_edge,0,sizeof(mst_edge)); for (i = 1; i < maxn; ++i) { father[i] = i; } int k = 0; for (i = 1; i <= m && k < n - 1; ++i) { //枚举有序边集中的每一条边 int fx = find(e[i].begin); int fy = find(e[i].end); if (fx != fy) { //若第k条边的两个端点i,j 分别属于两颗不同的子树 father[fx] = fy; //则将节点i所在的子树并入节点j所在的子树中 sum += e[i].weight; mst_edge[++k] = i;//***用来存储最小生成树中的边的序号 } } /** * 以下代码是求解最小生成树唯一性问题的核心代码,其算法思想如下: * 在生成的最小生成树的基础上,一次删除每一条边,生成次小生成树, * 然后判断次小生成树的最小边权值是否==最小生成树的最小边权值, * 如果相等,则证明最小生成树不唯一,否则证明所生成的最小生成树唯一 * */ int j; for (i = 1; i <= n - 1; ++i) { for (k = 1; k < maxn; ++k) { father[k] = k; } int ans2 = 0, k2 = 0; for (j = 1; j <= m; ++j) { if (j == mst_edge[i]) {//模拟删除最小生成树的某一条边 continue; } int fx = find(e[j].begin); int fy = find(e[j].end); if (fx != fy) { //若第k条边的两个端点i,j 分别属于两颗不同的子树 father[fx] = fy; //则将节点i所在的子树并入节点j所在的子树中 ans2 += e[j].weight; ++k2; } } if (k2 != n - 1) {//无法产生生成树 continue; } if (ans2 == sum) {//最小生成树不唯一 return -1; } } return sum; } bool compare(const edge& a, const edge& b) { return a.weight < b.weight; } //以上是用kruscal算法来解决问题的基本模板..... int main() { int t; scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); int i, j; for (i = 1; i <= m; ++i) { scanf("%d%d%d", &e[i].begin, &e[i].end, &e[i].weight); } sort(e + 1, e + m + 1, compare); //kruscal算法要求边有序 int sum = kruscal(m); if(sum == -1){ printf("Not Unique!\n"); }else{ printf("%d\n",sum); } } return 0; }