次小生成树

别人都写最小生成树,我来个次小的,您可别看是小,可是比最小生成树难了不少。(虽然可能对于某个强的了不得的大佬(点击进入翻译)来说,他应该会很“傲慢”的说:“这不很简单吗”)     当时五一我在QBXT上课的时候这个题我想了几想我才想出来的。

下面我给出代码(不会前向星和并查集的去看我的其他博客)

以下代码是在Kruskal算法的基础上进行修改,加入对x,y两点在最小生成树上路径中最长边的计算,存入length[ ][ ]数组。使用链式前向星记录每个集合都有哪些点。为了合并方便,除了head[ ]记录每条邻接表的头节点的位置外,end[ ]记录每条邻接表尾节点的位置便于两条邻接表合并。mst为最小生成树的大小,seemst为次小生成树的大小。在存在权值相同的边的情况下,seemst有可能等于mst。

并查集部分代码略详见我的另一篇博客(在此只给出声明)

1 //并查集部分 
2 const int maxn = 1010;
3 int UFSTree[maxn];
4 int find(int x);
5 void merge(int x , int y);
并查集

下面是Kruskal部分

 1 //kruskal
 2 const int maxe = 100010;
 3 struct node{
 4     int a , b;
 5     int w;
 6     bool select;
 7 }edge[maxe];
 8 
 9 bool cmp(node a , node b){
10     if(a.w != b.w) return a.w < b.w;
11     if(a.a != b.a) return a.a < b.a;
12     return a.b < b.b;
13 } 
14 
15 //链式前向星的数据结构
16 struct node1{
17     int to;
18     int next;
19 }; 
20 
21 node1 link[maxn];        //边数组 
22 int il;                    //边数组中数据的个数 
23 int head[maxn];            //邻接表的头节点位置 
24 int end[maxn];            //邻接表的尾节点位置 
25 int length[maxn][maxn];    //每两点在最小生成树上路径中最长的边
26 void kruskal(node * edge , int  n , int m){
27     int k = 0;
28     int  i , x , y;
29     int w , v;
30     /* 初始化邻接表,对于每个节点添加一条指向其自身的边,
31     表示以i为代表元的集合只有点i */
32     for(il = 0 ; il < n ; il ++){
33         link[il].to = il + 1;
34         link[il].next = head[il + 1];
35         end[il + 1] = il;
36         head[il + 1] = il;
37     } 
38     sort(edge + 1 , edge + 1 + m , cmp);
39     for(i = 1 ; i <= m ; i ++){
40         if(k == n - 1)break;
41         if(edge[i].w < 0)continue;
42         x = find(edge[i].a);
43         y = find(edge[i].b);
44         if(x != y){
45             //修改部分,遍历两个节点所有的集合
46             for(w = head[x] ; w != -1 ; w = link[w].next){
47                 for(v = head[y] ; v != -1 ; ){
48                     /*每次合并两个等价类的时候,分别属于两个等价类的两个
49                     点间的最长边一定是当前加入的边*/
50                     length[link[w].to][link[v].to] = length[link[v].to][link[w].to] = edge[i].w;
51                 }
52             } 
53             //合并两个邻接表
54             link[end[y]].next = head[x];
55             end[y] = end[x];
56             merge(x , y);
57             k ++;
58             edge[i].select = 1;
59         }
60     }
61 } 
62 int main(){
63     //先初始化和建图,然后进行下面的操作
64     int mst , secmst;
65     kruskal(edge , n , m );
66     mst = 0;
67     for(i = 1 ; i  <= m ; i ++){
68         if(edge[i].select)mst += edge[i].w;
69     }
70     secmst = INF;
71     for(i = 1 ; i <= m ; i ++){
72         if(! edge[i].select)secmst = min (secmst , mst + edge[i].w - length[edge[i].a][edge[i].b]);
73     }
74 }
Kruskal

整个算法运行了一次Kruskal 算法,时间复杂是O(m log m),同时又对整个length[ ][ ] 进行赋值, 时间复杂度为O(n²),最终又进行了时间复杂度为O(m)的遍历,总的时间复杂度为O(m log m + n²)。

大佬推荐ZHT大佬

posted @ 2017-05-07 21:29  秦时、长浩  阅读(298)  评论(1编辑  收藏  举报