題意:判斷圖中的最小產生樹是否唯一。
題解:只需驗是否存在兩個或兩個以上權值相同的最小產生樹。注意:1.圖中任意兩點間最多隻有一條無向邊; 2.圖可能不連通(此時mst = 0)。
Prime :複雜度 O( V ^ 2 )
#include <iostream>using namespace std;#define MAX 101#define INF 999999999#define max(a,b) (a>b?a:b)int dis[MAX], pre[MAX];int edge[MAX][MAX];int maxVal[MAX][MAX];bool inTree[MAX][MAX];bool vis[MAX];int Prime ( int n ){int i, j, k, minc, mst;for ( i = 1; i <= n; i++ ){dis[i] = edge[1][i];vis[i] = false;pre[i] = 1;}dis[1] = mst = 0; vis[1] = true;for ( i = 2; i <= n; i++ ) {minc = INF; k = -1;for ( j = 1; j <= n; j++ ){if ( ! vis[j] && dis[j] < minc ){minc = dis[j];k = j;}}if ( minc == INF ) return -1; // 圖不連通,沒有找到最小產生樹mst += minc;vis[k] = true;inTree[pre[k]][k] = inTree[k][pre[k]] = true; // 記錄加入的樹中的邊for ( j = 1; j <= n; j++ )if ( vis[j] == true ) maxVal[j][k] = max ( maxVal[j][pre[k]], edge[pre[k]][k] ); // 找j-k的路徑上權值最大的那條邊,並記錄在maxVal[j][k]中for ( j = 1; j <= n; j++ ){if ( ! vis[j] && dis[j] > edge[k][j] ){dis[j] = edge[k][j];pre[j] = k; // 修正前驅}}}return mst;}void initial ( int n ){for ( int i = 1; i < n; i++ ){for ( int j = i + 1; j <= n; j++ ){edge[i][j] = edge[j][i] = INF;inTree[i][j] = inTree[j][i] = 0;maxVal[i][j] = maxVal[j][i] = 0;}}}int main(){int t, n, m, u, v, w;scanf("%d",&t); while ( t-- ){scanf("%d%d",&n,&m);initial ( n );while ( m-- ){scanf("%d%d%d",&u,&v,&w);edge[u][v] = edge[v][u] = w;}int mst = Prime ( n );if ( mst < 0 ) { printf("0\n"); continue; }int res;bool flag = false;for ( int i = 1; i < n; i++ ){for ( int j = i + 1; j <= n; j++ ){if ( inTree[i][j] || edge[i][j] == INF ) continue; // 邊edge[i][j]在樹中或者i,j之間無邊res = mst + edge[i][j] - maxVal[i][j]; // 用邊edge[i][j], 替換i-j路徑上權值最大的那條邊,得到一棵新的產生樹if ( res == mst ) { flag = true; break; }}if ( flag ) break;}if ( flag )printf("Not Unique!\n");elseprintf("%d\n",mst);}return 0;}
Kruskal :複雜度 O(E*logE + V*E)
#include <algorithm>#include <iostream>using namespace std;#define MAX 10000struct Edge{int u, v, w;} edge[MAX];int father[MAX];int rank[MAX]; // 記錄每個集合的元素個數int mst[MAX]; // 記錄最小產生樹的邊int cmp ( const void *a, const void *b ){Edge *c = (Edge*)a;Edge *d = (Edge*)b;return c->w - d->w;}int find_set ( int x ){if ( father[x] != x )father[x] = find_set ( father[x] );return father[x];}int Kruskal ( int n, int m ){int fu, fv, i, sum = 0;for ( i = 1; i <= n; i++ ){father[i] = i;rank[i] = 1;}for ( i = 1; i <= m; i++ ){fu = find_set(edge[i].u);fv = find_set(edge[i].v);if ( fu == fv ) continue;sum += edge[i].w;mst[++mst[0]] = i;father[fu] = fv;rank[fv] += rank[fu];if ( rank[fv] == n || rank[fu] == n )return sum;}return 0;}int solve ( int n, int m ){qsort ( edge+1, m, sizeof(Edge), cmp );memset(mst,0,sizeof(mst));int res = Kruskal ( n, m );if ( res == 0 ) return 0;int fu, fv, k, i, sum;for ( k = 1; k <= mst[0]; k++ ) // 枚舉最小產生樹中的每一條邊,將其刪去,並求最小產生樹{sum = 0;for ( i = 1; i <= n; i++ ){father[i] = i; rank[i] = 1;}for ( i = 1; i <= m; i++ ){if ( i == mst[k] ) continue;fu = find_set(edge[i].u);fv = find_set(edge[i].v);if ( fu == fv ) continue;sum += edge[i].w;father[fu] = fv;rank[fv] += rank[fu];if ( rank[fv] == n || rank[fu] == n ) break;}if ( sum == res ) return -1;}return res;}int main(){int t, n, m;scanf("%d",&t); while ( t-- ){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);int ans = solve ( n, m );if ( ans == -1 )printf("Not Unique!\n");elseprintf("%d\n",ans);}return 0;}