题目大意就是问是否有多个权值相同的最小生成树
有两种方法, 一种是枚举删边,然后接着构造最小生成树,但是复杂度比较大
另外一种就比较好了 , 是求次小生成树的方法, 把生成树上任意两点间的最大边在求最小生成树的同时预处理出来,然后n2的枚举任意两点,如果这两点在最小生成树中不是相邻的,就可以删掉两点间的最大边,换上新边,即他俩之间的直接的边,在邻接矩阵中就是他们的距离。
第一种方法 ,我用克鲁斯卡尔做的,很久前的代码了
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 1005 #define INF 100000000 #define eps 1e-7 using namespace std; struct wwj { int u, v, w; int equal; int used; int del; }edge[10005]; int parent[10005], num[10005]; int n, m; bool first; bool cmp(wwj x, wwj y) { return x.w < y.w; } void init() { scanf("%d%d", &n, &m); for(int i = 1; i <= m; i++) { scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w); edge[i].del = 0; edge[i].equal = 0; edge[i].used = 0; } for(int i = 1; i <= m; i++) { for(int j = 1; j <= m; j++) { if(i == j) continue; if(edge[j].w == edge[i].w) edge[i].equal = 1; } } sort(edge + 1, edge + m + 1, cmp); first = true; } int find(int x) { if(parent[x] == x) return x; int t = find(parent[x]); parent[x] = t; return t; } void join(int x, int y) { int fx = find(x); int fy = find(y); if(fx != fy) { if(num[fx] > num[fy]) { parent[fy] = fx; num[fx] += num[fy]; num[fy] = 1; } else { parent[fx] = fy; num[fy] += num[fx]; num[fx] = 1; } } } int kruskal() { int i, sum = 0, cnt = 0, u, v; for(i = 1; i <= n; i++) parent[i] = i; memset(num, 1, sizeof(num)); for(i = 1; i <= m; i++) { if(edge[i].del == 1) continue; u = edge[i].u; v = edge[i].v; if(find(u) != find(v)) { sum += edge[i].w; cnt++; join(u, v); if(first) edge[i].used = 1; } if(cnt >= n - 1) break; } return sum; } void solve() { int w1 = kruskal(), w2, i; first = false; for(i = 1; i <= m; i++) { if(edge[i].used && edge[i].equal) { edge[i].del = 1; w2 = kruskal(); if(w1 == w2) { printf("Not Unique!\n"); return; } edge[i].del = 0; } } if(i > m) printf("%d\n", w1); } int main() { #ifdef LOCAL freopen("d:/data.in","r",stdin); freopen("d:/data.out","w",stdout); #endif int t; scanf("%d", &t); while(t--) { init(); solve(); } return 0; }
第二种方法是用prim做的
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #define MAXN 1005 #define MAXM 100005 #define INF 1000000000 using namespace std; int mx[105][105]; int d[105][105]; int can[105][105]; int dis[105], used[105], near[105]; int n, m; void init() { for(int i = 1; i <= n; i++) { near[i] = 1; for(int j = 1; j <= n; j++) { mx[i][j] = 0; d[i][j] = INF; can[i][j] = 0; } d[i][i] = 0; } memset(used, 0, sizeof(used)); } int main() { int T, x, y, w; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); init(); for(int i = 1; i <= m; i++) { scanf("%d%d%d", &x, &y, &w); d[x][y] = d[y][x] = w; } for(int i = 1; i <= n; i++) dis[i] = d[1][i]; used[1] = true; near[1] = -1; int sum = 0; for(int i = 1; i < n; i++) { int mi = INF; int v = -1; for(int j = 1; j <= n; j++) { if(near[j] != -1 && dis[j] < mi) { v = j; mi = dis[j]; } } int pre; if(v != -1) { pre = near[v]; sum += d[v][near[v]]; can[v][near[v]] = can[near[v]][v] = 1; mx[near[v]][v] = mx[v][near[v]] = d[v][near[v]]; near[v] = -1; for(int j = 1; j <= n; j++) { if(near[j] != -1 && d[v][j] < dis[j]) { dis[j] = d[v][j]; near[j] = v; } } } for(int j = 1; j <= n; j++) { if(near[j] == -1 && j != v) { mx[j][v] = mx[v][j] = max(mx[j][pre], mx[pre][v]); } } } bool flag = false; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { if(i != j && !can[i][j] && d[i][j] < INF) { int sum2 = sum - mx[i][j] + d[i][j]; if(sum2 == sum) flag = true; } } if(flag) printf("Not Unique!\n"); else printf("%d\n", sum); } return 0; }