思路:最小生成树+次小生成树
分析:判断最小生成树是否是唯一的,那就可以去判断次小生成树是否大于最小生成树。这里就涉及到了次小生成树的求法
求解次小生成树:
step 1. 先用prim求出最小生成树T.
在prim的同时,用一个矩阵max[u][v] 记录 在T中连结任意两点u,v的唯一的
路中权值最大的那条边的权值. (注意这里).
这是很容易做到的,因为prim是每次增加一个结点s, 而设已经标号了的结点
集合为W, 则W中所有的结点到s的路中的最大权值的边就是当前加入的这条边.
step 1 用时 O(V^2).
step 2. 枚举所有不在T中的边uv, 加入边uv则必然替换权为max[u][v]的边。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define MAXN 1010 #define INF 0XFFFFFFF int t , n , m; int vis[MAXN]; int pre[MAXN];/*记录节点的上一个节点*/ int lowcost[MAXN]; int maxvalue[MAXN][MAXN]; int G[MAXN][MAXN]; int mark[MAXN][MAXN];/*标记还有哪些边没被用*/ /*初始化*/ void init(){ memset(mark , 0 , sizeof(mark)); memset(maxvalue , 0 , sizeof(maxvalue)); for(int i = 1 ; i <= n ; i++){ pre[i] = 1;/*把所有点的上一点都记录为1*/ for(int j = 1 ; j <= n ; j++) G[i][j] = INF; } } /*prime算法*/ void prime(){ /*求解最小生成树*/ memset(vis , 0 , sizeof(vis)); int ans , pos , min; ans = 0; vis[1] = 1; for(int i = 1 ; i<= n ; i++) lowcost[i] = G[1][i]; for(int i = 1 ; i <= n ; i++){ pos = -1; for(int j = 1 ; j <= n ; j++){ if(!vis[j] && (pos == -1 || lowcost[j] < lowcost[pos])) pos = j; } vis[pos] = 1; ans += lowcost[pos]; mark[pre[pos]][pos] = mark[pos][pre[pos]] = 0; for(int j = 1 ; j <= n ; j++){ if(vis[j])/*记录maxalue数组*/ maxvalue[j][pos] = maxvalue[pos][j] = lowcost[pos]; if(!vis[j] && lowcost[j] > G[j][pos]){ lowcost[j] = G[j][pos]; pre[j] = pos;/*记录这些点的上一个节点*/ } } } /*求解次小生成树*/ min = INF; for(int i = 1 ; i <= n ; i++){ for(int j = 1 ; j <= n ;j++){ if(mark[i][j]){ int tmp = ans-maxvalue[i][j]+G[i][j]; if(tmp < min) min = tmp; mark[i][j] = mark[j][i] = 0; } } } if(ans < min)/*如果不同则说明最小生成树是唯一的*/ printf("%d\n" , ans); else printf("Not Unique!\n"); } int main(){ int x , y , v; scanf("%d" , &t); while(t--){ scanf("%d%d" , &n , &m); init(); for(int i = 0 ; i < m ; i++){ scanf("%d%d%d" , &x , &y , &v); if(G[x][y] > v){ G[x][y] = G[y][x] = v; mark[x][y] = mark[y][x] = 1; } } prime(); } return 0; }
时间: 2025-01-27 16:45:20