zoukankan      html  css  js  c++  java
  • 最小树形图(朱刘算法)

    不好意思 时间比较短,下面应该还会有修订的= = , 那段话是我复制过来的,觉得挺好的就用一下.

    下面是讲解(不理解一的时候 , 可以看看二 ,结合图片):

    :   最小树形图,就是给有向带权图中指定一个特殊的点root,求一棵以root为根的有向生成树T,并且T中所有边的总权值最小。最小树形图的第一个算法是 1965年朱永津和刘振宏提出的复杂度为O(VE)的算法。
           判断是否存在树形图的方法很简单,只需要以v为根作一次图的遍历就可以了,所以下面的 算法中不再考虑树形图不存在的情况。
    在所有操作开始之前,我们需要把图中所有的自环全都清除。很明显,自环是不可能在任何一个树形图上的。只有进 行了这步操作,总算法复杂度才真正能保证是O(VE)。
    首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小 入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这 个有向环所称一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。为什么入边的权要减去in[u],这个后面会解释,在这里先给出算法的步骤。然后可以证明,新图中最小树形图的权加上旧图中被收缩 的那个环的权和,就是原图中最小树形图的权。
            上面结论也不做证明了。现在依据上面的结论,说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边 in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w'(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除 掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们 得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。
    如果实现得很聪明的话,可以达到找最小入边O(E),找环 O(V),收缩O(E),其中在找环O(V)这里需要一点技巧。这样每次收缩的复杂度是O(E),然后最多会收缩几次呢?由于我们一开始已经拿掉了所有的 自环,我门可以知道每个环至少包含2个点,收缩成1个点之后,总点数减少了至少1。当整个图收缩到只有1个点的时候,最小树形图就不不用求了。所以我们最 多只会进行V-1次的收缩,所以总得复杂度自然是O(VE)了。由此可见,如果一开始不除去自环的话,理论复杂度会和自环的数目有关。

    :     最开始的图,把所有的最小入边都累加到ret里。至于为什么,因为这样才能保证所得的ret有可能是最小树形图的解,当然,是在这些最小入边集合不行成环得情况下。
    如果有了环,ret肯定不是最终答案,因为环中间有的边需要删掉,而且环之间也要连接起来。现在我们无法得知删除环中的哪些边才行。这就需要建立新图了。
              举个例子:某个图的部分图中, 1->2权值为3, 2->1权值为4, 3->1权值为9, 4->2权值为7。 那么可以看到,结点1和结点2是形成了一个环的。我们仅从其大小不知道删除哪条边比较好,这时看到3->1权值为9, 如果走这条边,那么接下来只能删除掉2->1这条边,同理走4->2的话就要删除掉1->2这条边。 那么就不妨建立新图, 将1和2缩成一点,3->1的权值就变成了9-4=5, 4->2的权值变成了7-3=4。 这样的话,就相当于变相删除了不需要走的边了。形成新图后,又变成了最小树形图的求解,就这样循环下去,直到图中的最小边集没有环为止。

          最小树形图

    下面是模板代码:

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 
     5 using namespace std;
     6 const int MAXN = 1e4 , INF = 1e8;
     7 int d[MAXN] , id[MAXN] , vis[MAXN] , pre[MAXN]; //d:除root点外每个点的最小入边 id:下一次建图新的节点号 vis:用来判断是否成环 下面程序见 pre:点的前序节点
     8 int V , E;    // V:点的个数    E:边的个数
     9 struct node {
    10     int u , v , cost;  //边的起点  终点  以及长度
    11 }edge[MAXN];
    12 
    13 int zhuliu(int root) {
    14     int res = 0;   //最小树形图的长度
    15     while(true) {
    16         for(int i = 0 ; i < V ; i++) {
    17             d[i] = INF;
    18         }
    19         for(int i = 0 ; i < E ; i++) {    //寻找最小入边
    20             int u = edge[i].u , v = edge[i].v;
    21             if(u != v && edge[i].cost < d[v]) {
    22                 pre[v] = u;
    23                 d[v] = edge[i].cost;
    24             }
    25         }
    26         for(int i = 0 ; i < V ; i++) {
    27             if(i != root && d[i] == INF) {        //除了root之外  有别的点无最小入边
    28                 return -1;               
    29             }
    30         }
    31         int cont = 0;
    32         memset(id , -1 , sizeof(id));
    33         memset(vis , -1 , sizeof(vis));
    34         d[root] = 0;
    35         for(int i = 0 ; i < V ; i++) {   //找环
    36             res += d[i];
    37             int v = i;
    38             //vis[v] == i 表明找到一个环    id[v] != -1 表明这个点在循环中已经被下面的操作缩点(在环中)    v == root 说明寻找到了根节点
    39             while(vis[v] != i && id[v] == -1 && v != root) {    //每个点寻找前序节点  要么找到根部  要么找到一个环
    40                 vis[v] = i;
    41                 v = pre[v];
    42             }
    43             if(v != root && id[v] == -1) {   //成环 缩点
    44                 for(int u = pre[v] ; u != v ; u = pre[u]) {
    45                     id[u] = cont;
    46                 }
    47                 id[v] = cont++;
    48             }
    49         }
    50         if(cont == 0) {   //无环  break
    51             break;
    52         }
    53         for(int i = 0 ; i < V ; i++) {
    54             if(id[i] == -1) {    //没有成环的点
    55                 id[i] = cont++;
    56             }
    57         }
    58         for(int i = 0 ; i < E ; i++) {   //重新建图 重新标记
    59             int u = edge[i].u , v = edge[i].v;
    60             edge[i].u = id[u] , edge[i].v = id[v];
    61             if(id[v] != id[u]) {   
    62                 edge[i].w -= d[v];  //理解上面的文字描述 > . < !(特别是二)
    63             }
    64         }
    65         V = cont;
    66         root = id[root];   //新的根
    67     }
    68 }
    69 
    70 int main()
    71 {
    72 
    73 }

    写的(盗用的)有些匆忙...

  • 相关阅读:
    springmvc学习笔记---idea创建springmvc项目
    Spring Boot中使用Swagger2构建RESTful API文档
    Dubbo原理实现之使用Javassist字节码结束构建代理对象
    Dubbo启动时服务检查
    Dubbo原理实现之代理接口的定义
    dubbo实现原理之动态编译
    Dubbo实现原理之基于SPI思想实现Dubbo内核
    dubbo实现原理之SPI简介
    Spring中使用StandardServletMultipartResolver进行文件上传
    SpringCloud之Feign
  • 原文地址:https://www.cnblogs.com/Recoder/p/5008678.html
Copyright © 2011-2022 走看看