次小生成树模板

我用自己的理解为大家讲一讲次小生成树的代码的理解吧,首先我们得知道最小生成树,简单的说就是给你 n 个点,然后让你找 n-1 条边,将 n 个点连通,然后他们的总距离是所有能实现 n-1 条边连通该图的最小的一种方法,那么可想而知,次小生成树就是第二优解,它也有可能和最小生成树的值一样,但是肯定不能比最小生成树的总距离要小,要不然就让它当最小生成树了,是吧?那我们也知道,其实次小生成树只要和最小生成树不一样就行,因为最小生成树是图的最优解,那么可想而知,次小生成树,我们只要在最小生成树上改变其中的一条边即可构成,如果我们在最小生成树上再添加一条边,那么肯定会构成回路的,所以我们就要在这个回路中减去构成最小生成树的最大的一条边即可。

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<math.h>
 5 using namespace std;
 6  
 7 #define INF 0x3f3f3f3f
 8 int v,e;
 9 int s[1005][1005],use1[1005][1005],maxn[1005][1005];
10 int sum;
11  
12 int prim()///求最小生成树的过程
13 {
14     int vis[1005],low[1005],pre[1005];
15     int bj,min1;
16     sum=0;
17     memset(vis,0,sizeof(vis));///将该初始化的数组初始化
18     memset(use1,0,sizeof(use1));
19     memset(maxn,0,sizeof(maxn));
20     vis[1]=1;/**次数组是标记数组,因为城市的编号是从1到n的,所以我门任意从一个点开始遍历,
21     一般方便,我门都会选择最小的编号,即为1的城市编号,该数组将用过得点都标记起来**/
22     bj=1;///这个变量是用来记录与当前节点距离最近的下一个城市得出编号
23     for(int i=2;i<=v;i++)///我门选择从1开始,于是将将剩下的编号与1的距离都存在low数组里
24     {
25         low[i]=s[bj][i];
26         pre[i]=1;///该数组是那个点到i点的,说白了与i相连的上一个节点
27     }
28     pre[1]=0;///将1的前驱节点设为0,因为他没有前驱节点
29     for(int i=1;i<v;i++)///找剩下的v-1个点,也可以说找v-1条边
30     {
31         min1=INF;///这里别忘了,每一轮都要刷
32         for(int j=1;j<=v;j++)///遍历v个点
33         {
34             if(!vis[j]&&min1>low[j])///如果该点没有被使用过并且路径能被更新
35             {
36                 bj=j;///记录编号
37                 min1=low[j];///更新值
38             }
39         }
40         sum+=min1; ///将找到的最小生成树的边加到最小生成树的总值里
41         vis[bj]=1; ///将找到的点标记
42         use1[bj][pre[bj]]=use1[pre[bj]][bj]=1;/**将构成最小生成树的边都标记一下,
43     待次小生成树找边的时候就不能找这些被标记的边**/
44         for(int j=1;j<=v;j++)///更新
45         {
46             if(vis[j])/**如果该点被标记过,也就是走过了,我们就要更新点bj与点j的maxn数组值,
47                 这里肯定会有宝宝们疑问为什么只更新走过的点呢,其实很简单,对于没有走过的点,
48                 就最小生成树中来说,你给他添加一条边,它也构不成回路,所以不用着急更新,这个数组
49                 的作用就是在找次小生成书的时候要减去环中的最大边用的**/
50                 maxn[j][bj]=maxn[bj][j]=max(maxn[j][pre[bj]],low[bj]);
51             if(!vis[j]&&low[j]>s[bj][j])/**这是用来更新low数组的,因为没有走过的点和走过的点任意一点相连
52             就可以实现连通,当然我们会找最小的距离喽,但是更新的时候别忘了,将前驱节点也要一并更新了**/
53             {
54                 low[j]=s[bj][j];
55                 pre[j]=bj;
56             }
57         }
58     }
59     return sum;
60 }
61  
62 int sprim()///求次小生成树
63 {
64     int ans=INF;///记录次小生成树的总值
65     for(int i=1;i<=v;i++)///遍历所有的边
66     {
67         for(int j=i+1;j<=v;j++)
68         {
69             if(!use1[i][j]&&s[i][j]!=INF)///如果该边不是构成最小生成树的边而且可以走的通
70             {
71                 ans=min(sum+s[i][j]-maxn[i][j],ans);///更新次小生成树,加上该边减去环中最大的边
72             }
73         }
74     }
75     return ans;
76 }
77  
78 int main()
79 {
80     int t,a,b,c;
81     scanf("%d",&t);
82     while(t--){
83         memset(s,INF,sizeof(s));///别忘记将该数组先刷大
84         scanf("%d %d",&v,&e);
85         for(int i=1;i<=e;i++){
86             scanf("%d %d %d",&a,&b,&c);
87             s[a][b]=s[b][a]=min(s[a][b],c);///双向连接,防止有重复输入路径的情况
88         }
89         if(prim()==sprim())///判断次小生成树和最小生成树的结果是否一样
90             printf("Yes\n");
91         else
92             printf("No\n");
93     }
94     return 0;
95 }

 

posted @ 2019-10-16 23:23  古比  阅读(260)  评论(0编辑  收藏  举报