生成树

prim算法和kruskal算法是解决生成树的算法,prim更快,但kruskal实现简单,

思路上是贪心:

prim:每次寻找离生成树最近的点,加入生成树,再用那个点松弛其他点到生成树的距离,一般是O(N2);当然,有优化,二叉堆可以到达O((E +V)logV),斐波那契堆可以优化到O(E + VlogV)

kruskal:给边排序,区最小的边加入就好,用并查集维护联通;时间复杂度为O(ElogE + E*A(V) + V);A是阿克曼函数的反函数,再加V因为并查集要初始化

证明我就不搞了,就是两个反证,意思一下就好啦。

板子还是要上的

 1 int prime(int n)
 2 {
 3     const int INF = 1<<29;
 4     int d[maxn];
 5     int k = 1;d[k] = 0;
 6     for(int i = 1;i <= n;i++) d[i] = G[1][i];
 7     int vis[maxn];int ans = 0;
 8     memset(vis,0,sizeof(vis));
 9     for(int i = 1;i < n;i++)
10     {    
11         vis[k] = 1;
12         int M =INF;int last = 0;
13         for(int j = 1;j <= n;j++)
14         {
15             if(d[j] < M&& !vis[j]){
16                 M =d[j];
17                 last = j;
18             }
19         }
20         if(M == INF) break;
21         ans += M;
22         k = last;
23         for(int j = 1;j <= n;j++)
24             if(j != k&&d[j] > G[k][j])
25                 d[j] = G[k][j];
26     }
27     return ans;
28 };
prim

斐波那契堆的不会:好吧,我还是会用STL的:

 1 struct HeapNode{
 2     int d,u;
 3     bool operator < (const HeapNode& rhs)const{
 4         return  d > rhs.d;
 5     }
 6 };
 7 int prime(int n)
 8 {
 9     int ans = 0;
10     priority_queue<HeapNode> Q;
11     Q.push((HeapNode){0,1});
12     for(int i = 0;i <= n;i++) d[i] = INF;
13     d[1] = 0;
14     Q.push((HeapNode){0,1});
15     int vis[maxn];
16     memset(vis,0,sizeof(vis));
17     while(!Q.empty()){    
18         HeapNode x =Q.top();Q.pop();
19         int k = x.u;
20         if(vis[k]) continue;
21         vis[k] = 1;
22         ans += x.d;
23         for(int j = 1;j <= n;j++)
24             if(j != k&&d[j] > G[k][j])
25             {
26                 d[j] = G[k][j];
27                 Q.push((HeapNode){d[j],j});
28         
29             }
30     }
31     return ans;
32 };
prim+堆优化
 1 int find(int x)
 2 {
 3     if(pa[x] == x) return x;
 4     return pa[x] = find(pa[x]);
 5 }
 6 int kruskal(int n,int m)
 7 {
 8     for(int i = 0;i <= n;i++) pa[i] = i;
 9     int ans = 0;
10     sort(edges.begin(),edges.end());
11     for(int i = 0;i < edges.size();i++)
12     {
13         Edge& e =edges[i];
14         int x = find(e.from);int y = find(e.to);
15         if(x != y){
16             pa[x] = y;
17             ans += e.dist;
18             printf("%d %d ",min(e.from,e.to),max(e.from,e.to ));
19         }        
20     }
21     return ans;
22 }
kruskal

建图部分就不要我再贴了吧

结论:瓶颈生成树 无向图G的一颗瓶颈生成树是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。瓶颈生成树的值为T中最大权值边的权。

无向图的最小生成树一定是瓶颈生成树,但瓶颈生成树不一定是最小生成树。
命题:无向图的最小生成树一定是瓶颈生成树。
证明:可以采用反证法予以证明。
假设最小生成树不是瓶颈树,设最小生成树T的最大权边为e,则存在一棵瓶颈树Tb,其所有的边的权值小于w(e)。删除T中的e,形成两棵数T', T'',用Tb中连接T', T''的边连接这两棵树,得到新的生成树,其权值小于T,与T是最小生成树矛盾。[1-2] 
命题:瓶颈生成树不一定是最小生成树。
下面是一个反例:
由红色边组成的生成树是最小瓶颈树,但并非最小生成树。
从上面可以看出,最小瓶颈生成树不止一条。
最小生成树是最小瓶颈生成树的一个子集。
POJ 2395
直接模板;
 1 #include<iostream>
 2 #include<cstring>
 3 #include<vector>
 4 #include<algorithm>
 5 #include<cstdio>
 6 
 7 
 8 using namespace std;
 9 
10 const int maxn = 101010;
11 int pa[maxn];
12 
13 struct Edge
14 {
15     int from,to,dist;
16     Edge(int from,int to,int dist) :from(from),to(to),dist(dist){}    
17     bool operator < (const Edge& b) const{
18         return dist < b.dist;
19     }
20 };
21 vector<Edge> edges;
22 int find(int x)
23 {
24     if(pa[x] == x) return x;
25     else return pa[x] = find(pa[x]);
26 }
27 
28 int kruskal(int n,int m)
29 {
30     for(int i = 1;i <= n;i++) pa[i] = i;
31     sort(edges.begin() ,edges.end());
32     int cnt = 0;
33     for(int i = 0;i < m;i++)
34     {
35         Edge& e = edges[i];
36         int x = find(e.from);int y = find(e.to);
37         if(x != y)
38         {
39             pa[x] = y;
40             cnt++;
41         }
42         if(cnt == n-1) return e.dist ;
43     }
44 }
45 
46 int main()
47 {
48     int n,m;
49     scanf("%d%d",&n,&m);
50     for(int i = 0;i < m;i++)
51     {
52         int from,to,dist;
53         scanf("%d%d%d",&from,&to,&dist);
54         edges.push_back(Edge(from,to,dist));  
55     }
56     printf("%d\n",kruskal(n,m));
57     return 0;
58 }
POJ 2395

 最小瓶颈生成树:先求最小生成树,处理出所有路径的最大路径,依次加不在最小生成树里的边,肯定形成环,去最长路径,扫描维护一下增量值,因为是自己想的,没做题来验证,估计又慢又丑还不对,还是贴下吧:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<cstdlib>
  7 using namespace std;
  8 
  9 struct Edge
 10 {
 11     int from,to,dist;
 12     Edge(int from,int to,int dist):from(from),to(to),dist(dist){}    
 13     bool operator < (const Edge& rhs)const{
 14         return dist < rhs.dist;
 15     }
 16 };
 17 const int maxn = 1000;
 18 vector<Edge> edges1;
 19 vector<int> G1[maxn];
 20 vector<Edge> edges2;
 21 vector<int> G2[maxn];
 22 int pa[maxn];
 23 int maxcost[maxn][maxn];
 24 int vis[maxn];
 25 int find(int x)
 26 {
 27     if(pa[x] == x) return x;
 28     else return pa[x] = find(pa[x]);
 29 }
 30 
 31 int kruskal(int n)
 32 {
 33     for(int i = 0;i <= n;i++) pa[i] = i;
 34     sort(edges1.begin(),edges1.end());
 35     int m = edges1.size();
 36     int ans = 0;int cnt = 0;
 37     for(int i = 0;i < m;i++)
 38     {
 39         Edge& e = edges1[i];
 40         int x = find(e.from);int y = find(e.to);
 41         if(x != y)
 42         {
 43             pa[x] = y;
 44             ans += e.dist;
 45             edges2.push_back(Edge(e.from,e.to,e.dist));
 46             edges2.push_back(Edge(e.to,e.from,e.dist));
 47             int m2 = edges2.size();
 48             G2[e.from].push_back(m2-2);
 49             G2[e.to].push_back(m2-1);
 50             cnt++;
 51         }
 52         if(cnt == n-1) return ans;
 53     }
 54 }
 55 
 56 void dfs(int from,int now,int maxdist)
 57 {
 58     maxcost[from][now] = maxdist;
 59     for(int i = 0;i < G2[now].size();i++)
 60     {
 61         Edge& e = edges2[G2[now][i]];
 62         if(vis[e.to]) continue;
 63         int mid = maxdist;
 64         maxdist = max(maxdist,e.dist);
 65         vis[e.to] = 1;
 66         dfs(from,e.to,maxdist);
 67         vis[e.to] = 0;
 68         maxdist = mid;        
 69     }
 70     return;
 71 }
 72 
 73 int main()
 74 {
 75     int n,m;
 76     scanf("%d%d",&n,&m);
 77     for(int i = 0 ;i < m;i++)
 78     {
 79         int from,to,dist;
 80         scanf("%d%d%d",&from,&to,&dist);
 81         edges1.push_back(Edge(from,to,dist));
 82         edges2.push_back(Edge(to,from,dist));
 83         int k = edges1.size();
 84         G1[from].push_back(k-2);
 85         G1[to].push_back(k-1);
 86     }
 87     int ans = kruskal(n);
 88     for(int i = 1;i <= n;i++)
 89     {
 90         vis[i] = 1;
 91         dfs(i,i,0);
 92         vis[i] = 0;
 93     }
 94     int add = 9999999;
 95     for(int i = 0;i < m;i++)
 96     {
 97         Edge& e = edges1[i];
 98         if(e.dist > maxcost[e.from][e.to])
 99         {
100             add = min(e.dist-maxcost[e.from][e.to],add);
101         }
102     }
103     cout<<ans<<endl;
104     cout<<ans+add<<endl;
105     return 0;
106 }
次小生成树

最小增量生成树,暂时不会

 最小树形图: 朱刘;妈的毒瘤,搞了好久还是不知道哪里挑错了。

 

posted @ 2017-02-23 16:40  rsqppp  阅读(180)  评论(0)    收藏  举报