P.S.我最近挖了好多坑啊~U·ェ·U 先讲一下蓝书上提到的关于Spanning Tree 生成树的内容。

2个性质:切割(连X-Y的边权最小的边必被生成树包含)、回路(任意回路/环上的边权最大的边必不被生成树包含)。

增量最小生成树:N点的空图上依次加入一共M条边,求每加入一条的MST最小生成树权值。
解法:1.O(m*nlog n)。每生成一个MST就删掉没有选的边,加入一条新边就做一次(N-1)+1=N条边的Kruskal算法。
2.O(mn)。加了一条边e=(u,v)后成了一个回路/环,据回路性质,找到这个回路里的最大边删掉就是解。而(u,v)必在这个回路中,便O(n)用dfs或bfs从u到v找这样的一条路径就可以了。

最小瓶颈生成树:给出加权无向图,求一棵最大边权值尽量小的生成树。
解法:由于关心最大边便把边从小到大排序,最先生成的那棵生成树就是答案。而这就是Kruskal算法,所以原图的最小生成树就是一棵最小瓶颈生成树了。
注意——最小瓶颈生成树不一定是最小生成树,但最小生成树一定是最小瓶颈生成树(这个前几行不就简单地证了吗)【bzoj2429】[HAOI2006]聪明的猴子(图论--最小瓶颈生成树 模版题)

最小瓶颈路:给出加权无向图的两个结点u和v,求从u到v的一条路径,使得路径上权值最大的边的权值尽量小。
解法:1.O(logd*m)(d表示最大的边权的最大值,然后...时间复杂度←我猜是这样的 (╯o╰))二分法+bfs,二分最大边的权值,bfs跑一遍看这样的最大边权是否可以找到一条从u到v的路径。
2.O(m*logm)。Kruskal也是由于关心最大边,便同求“最小瓶颈生成树”一样思考,边排序后,求到的第一条路径就是答案,便可以跳出来了!!不需要求完整颗生成树。因此最小瓶颈路便是最小生成树上的对应路径。
模版代码见:【uva 534】Frogger(图论--最小瓶颈路 模版题)

最小瓶颈路(多组询问):给出加权无向图,求每两个结点u和v之间的最小瓶颈路的最大边长f(u,v)。
解法:相似地,先求出MST。再用dfs把MST变成有根树,每新访问一个结点v / 一条边(u,v)就更新之前求过的所有点。f(x,v)=f(v,x)=max(f(x,u),w(u,v))。这里是O(n2)。

次小生成树求权值之和第二小的生成树的权值和。
解法:先求出MST。再枚举新加的一条边(u,v),这时成环便去掉(u,v)所在环上,即u到v的路径上的权值最大的边(不删(u,v)这个新增的边)。这样就是答案了。【原因是:次小生成树一定至少有一条边与最小生成树不一样,那么存在不同于最小生成树的生成树中权值和最小的生成树就是次小生成树啦。】于是,我们可以像上一个问题一样处理出每对结点(u,v)的最小瓶颈路的最大边长f(u,v)。这样O(m log m)和O(n2)之后,就是由MST加一条边,删一条边(“边交换”)O(m)枚举m-n+1条加的边,最后O(1)算出新生成树的权值。总时间复杂度为O(m log m+n2+m)。
模版代码见:【uva 10600】ACM Contest and Blackout(图论--次小生成树 模版题)

蓝书上还提到一个最小有向生成树,我还是不学了。(.=^・ェ・^=)

----------------------------------------------------------------------------------------------------------------------------------------------

接着是一篇博文:原博——http://blog.csdn.net/fuyukai/article/details/51321680

P.S.同样的,我略加修改。下面是原文——

简介:
最小生成树是图论里面一类经典问题,可以有很多种变形,其中最小瓶颈路和次小生成树就是两种比较经典的变形。最小瓶颈路就是在两个结点之间求一条最长边最短的路径,而次小生成树则是所有生成树中权值排名第二的生成树(可以和最小生成树相等)。下面我们分别来看看这两个问题。

最小瓶颈路:
给定一个加权无向图,并给定无向图中两个结点u和v,求u到v的一条路径,使得路径上边的最大权值最小。这个问题可以稍微加强一下,即求很多对结点之间的最小瓶颈路。
无向图中,任意两个结点的最小瓶颈路肯定在最小生成树上。因此,对于第一个问题,我们可以先求出最小生成树,然后从结点u对最小生成树进行DFS直到访问到结点v,DFS过程中就可以求出最长边。这种方法非常简单,但是效率就不够高,如果结点对很多的话,我们每次都对最小生成树进行DFS就会很慢了(一次时间O(n))。
我们可以在查询前进行预处理,将所有结点对的最长边保存在一个maxcost数组中,之后每次查询直接访问数组即可,只需要O(1)时间。具体的方法是将无根的最小生成树转成有根树,转换过程中同时计算maxcost[u][v]。当访问一个新结点u时,考虑所有已经访问过的结点j,对maxcost[j][u]进行更新,公式如下:
maxcost[j][u]=max(maxcost[j][fa[u]], maxcost[j][u])
其中,fa数组中保存结点在有根树中的父节点。
代码实现:
代码实现有两种具体方式,一种基于prim算法,一种则为kruskal算法。
Prim算法:
如果使用prim算法,那么我们可以在求解最小生成树的过程中将有根树建立起来,并且同时求所有结点对maxcost。

 1 int prim(int s)
 2 {
 3     int res=0;
 4     memset(maxcost,0,sizeof(maxcost));
 5     for(int i=1;i<=n;i++)
 6         vis[i] = 0, d[i] = INF, pre[i]=i;
 7 
 8     d[s]=0;
 9     for(int i=0;i<n;i++)
10     {
11         int maxx=INF, index=-1;
12         for(int j=1;j<=n;j++)
13         {
14             if(!vis[j]&&d[j]<maxx)
15             {
16                 maxx=d[index=j];
17             }
18         }
19         if(index==-1)
20             break;
21 
22         for(int j=1;j<=n;j++)
23             if(vis[j])
24                 maxcost[index][j] = maxcost[j][index] = 
25                     max(maxcost[pre[index]][j], maxx);
26 
27         res+=maxx;
28         vis[index]=1;
29 
30         for(int j=1;j<=n;j++)
31         {
32             if(!vis[j]&&g[index][j]<d[j])
33             {
34                 d[j] = g[index][j];
35                 pre[j] = index;
36             }
37         }
38     }
39     return res;
40 }
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-31
  • 2021-08-08
  • 2021-12-29
  • 2021-08-20
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-06-04
  • 2021-07-23
  • 2022-12-23
  • 2022-01-01
  • 2021-10-31
相关资源
相似解决方案