这题可以用次小生成树解,这里用Kruskal算法来做。每条边除维护u,v,w外,还维护:
used:表示这条边是否加过
eq:表示有没有与这条边相等的边
del:删除标记,以便删边之用
如果对于一个最小生成树的的边A,有一条与之权值相等的边B,则考虑把A删掉,再求一次最小生成树,看求出的总权值是否与前一个最小生成树的总权值相等。如果相等,则不唯一,如果找遍了这些权值相等的边都没找到,就说明唯一(注意每次不相等的话要把A重新加进来)。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 107 struct Edge { int u,v,w; bool eq,del,used; }edge[N*N]; int cmp(Edge ka,Edge kb) { return ka.w < kb.w; } int fa[N],n,m,first; int findset(int x) { if(x != fa[x]) fa[x] = findset(fa[x]); return fa[x]; } int Kruskal() { int i,j,k = 0; int sum = 0; for(i=1;i<=n;i++) fa[i] = i; for(i=1;i<=m;i++) { if(edge[i].del) continue; int u = edge[i].u; int v = edge[i].v; int fx = findset(u); int fy = findset(v); if(fx != fy) { sum += edge[i].w; fa[fx] = fy; if(first) edge[i].used = 1; k++; } if(k >= n-1) break; } return sum; } int main() { int t,i,j,u,v,w; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); edge[i].u = u; edge[i].v = v; edge[i].w = w; edge[i].del = edge[i].used = edge[i].eq = 0; } for(i=1;i<=m;i++) { for(j=1;j<=m;j++) { if(i == j) continue; if(edge[i].w == edge[j].w) edge[i].eq = 1; } } sort(edge+1,edge+m+1,cmp); first = 1; int w1 = Kruskal(); int w2; first = 0; for(i=1;i<=m;i++) { if(edge[i].used && edge[i].eq) { edge[i].del = 1; w2 = Kruskal(); if(w1 == w2) { puts("Not Unique!"); break; } edge[i].del = 0; } } if(i > m) printf("%d ",w1); } return 0; }