【模板】 次小生成树

 1 /*for(遍历图中所有不属于最小生成树的边)
 2     把当前边加入最小生成树产生回路
 3     去掉回路中除当前边之外权值最大的边
 4     记录下现在的树及其总权值
 5 在上面循环产生的树中选一棵总权值最小的,就是次小生成树*/
 6 /*上面做法只遍历一次最小生成树,但是如果枚举删去最小生成树上的边,那就要求n-2次最小生成树了,所以不可取*/
 7 #include <cstdio>
 8 #include <cstring>
 9 #include <algorithm>
10 #include<queue>
11 using namespace std;
12 typedef pair<int,int> pii;
13 const int maxn = 111;
14 const int inf = 0x3f3f3f3f;
15 int g[maxn][maxn];//邻接矩阵存图
16 int Max[maxn][maxn];//表示最小生成树中i到j的最大边权
17 bool used[maxn][maxn];//判断该边是否加入最小生成树
18 int pre[maxn];//点在最小生成树上的前驱
19 int dis[maxn];//在已存的点集中,到各个点的最短边
20 bool vis[maxn];//判断该点是否加入最小生成树
21 int T,n,m;
22 int prim()
23 {
24     int sum= 0;
25     memset(vis, false, sizeof(vis));
26     memset(used, false, sizeof(used));
27     memset(Max, 0, sizeof(Max));
28     memset(dis,0x3f3f3f3f,sizeof(dis));
29     dis[1]=0;
30     pre[1]=0;
31     priority_queue<pii,vector<pii>,greater<pii> >q;
32     q.push(make_pair(0,1));
33     int all=0;
34     while(!q.empty()){
35         int u=q.top().second;
36         q.pop();
37         if(vis[u]) continue;
38         vis[u]=1;
39         //就是只有当我确定了u这个点进入了最小生成树,我才改used数组和Max,之间都只是根据dis修改pre;
40         used[u][pre[u]]=used[pre[u]][u]=1;
41         ++all;
42         sum+=dis[u];
43         for(int j=1;j<=n;++j){
44             if(vis[j]) {
45                 if(j!=u) Max[j][u]=Max[u][j]=max(Max[j][pre[u]],dis[u]);
46                 else Max[j][u]=0;
47             //相当于一个dp,求出j到u之间最长边的边权.
48             }
49             else if(g[j][u]!=0x3f3f3f3f&&dis[j]>g[j][u]){
50                 dis[j]=g[j][u];
51                 pre[j]=u;
52                 q.push(make_pair(dis[j],j));
53             }
54         }
55     }
56     if(all<n) return -1;
57     return sum;//最小生成树的权值之和
58 }
59 int smst(int sum)//sum是最小生成树的权值和
60 {
61     int ans=0x3f3f3f3f;
62     for (int i = 1; i <= n; i++)//枚举最小生成树之外的边
63         for (int j = i + 1; j <= n; j++)
64             if (g[i][j]!=0x3f3f3f3f&&!used[i][j])
65             //加一条连接i和j(保证之后可以构成生成树)的且不在最小生成树的边,再删去i和j中最大边权的边
66                 ans=min(ans,sum+g[i][j]-Max[i][j]);
67     if (ans==0x3f3f3f3f) return -1;
68     return ans;
69 }
70 int main()
71 {
72     scanf("%d", &T);
73     while (T--)
74     {
75         scanf("%d %d", &n, &m);
76         memset(g,0x3f3f3f3f,sizeof(g));
77         for(int i=1;i<=n;++i) g[i][i]=0;
78         int u,v,w;
79         for(int i=1;i<=m;++i){
80             scanf("%d%d%d",&u,&v,&w);
81             g[u][v]=g[v][u]=min(g[u][v],w);
82         }
83         int ans=prim();
84         if(ans==-1) puts("Not Unique!");
85         else if(smst(ans)==ans) puts("Not Unique!");
86         else printf("%d\n",ans);
87         //题目要判断最小生成树是否唯一
88     }
89     return 0;
90 }

猜你喜欢

转载自www.cnblogs.com/xiaobuxie/p/11391892.html
今日推荐