zoukankan      html  css  js  c++  java
  • zkw神树优化dijkstra

    zkw神树

    zkw神树不多说了,不懂的移步我的博客《zkw线段树》。这里优化dij用,不需要区间查询,只使用点修改一个,编程复杂度极低。

    唯一不同的是的为了更快找到最小值的位置(dijkstra中点的编号),需要加一个mark标记,记录最小值逻辑上的位置,或者说图里面点的编号。

    奉上一个做好的zkwheap模板,不光在这里能用,是个能修改元素值的堆。

    template < typename T > struct ZkwHeapNode {
      T value;
      size_t mark;
      // 编号
      ZkwHeapNode() { }
      ZkwHeapNode(const T & _value):value(_value) {}
    };
    
    template < typename T, typename Comp > class ZkwHeap {
    
    private:
      typedef ZkwHeapNode < T > Node;
      typedef ZkwHeap < T, Comp > Heap;
    
      Comp cmp;
      Node *NodeList;
      size_t n;
      // 最多元素
      int init_value;
      // 初始值,大根堆(less<>)就用无穷小,小根堆(greater<>)就用无穷大
    
      void fix(size_t _pos) {
        if (cmp(NodeList[_pos << 1].value, NodeList[(_pos << 1) + 1].value))
          NodeList[_pos] = NodeList[(_pos << 1) + 1];
        else
          NodeList[_pos] = NodeList[_pos << 1];
      }
    
    public:
    
      ZkwHeap(size_t _MaxN, const T & _init_value):init_value(_init_value) {
        n = 1 << (1 + (size_t) (log(_MaxN) / log(2.0)));
        NodeList = new Node[n << 1];
        for (size_t i = 1; i <= n + n - 1; i++)
          NodeList[i].value = init_value;
        for (size_t i = n; i <= n + n - 1; i++)
          NodeList[i].mark = i - n + 1;
         // 分配标记
      }
    
      ~ZkwHeap() {
        delete[]NodeList;
      }
    
      T top() {
        return NodeList[1].value;
      }
      // 最大元素
    
      T top_pos() {
        return NodeList[1].mark;
      }
      // 最大元素位置
    
      void modify(size_t _position, const T & _new_value) {
        int _pos = _position + n - 1;
        NodeList[_pos].value = _new_value;
        while (_pos)
          fix(_pos >>= 1);
      }
    
      T pop() {
        T return_value = NodeList[1].value;
        modify(NodeList[1].mark, init_value);
        return return_value;
        // 删除极值
      }
    };

    理解了zkw线段树,也就很容易明白为什么用zkw线段树优化dijkstra了。

    堆优化dijkstra分析

    dijkstra算法效率是Θ(N^2)。事实上,运用邻接表+堆优化可是将时间复杂度降到O(NlgE)。我们将在最后用聚合分析得出这个界。
    为什么要堆优化?因为dijkstra最大的时间复杂度消耗在寻找dis[]中的最小值上。而用小根堆就解决了这个问题。伪代码如下:

    function dijkstra(G, s) {
        H = a new heap
        dis = a new array contain ∞
        H中s的值设为0
        for (int i=2; i<=G.n; i++) {
            k = H中dis最小的点
            dis[k] = k.dis
            // 记录下来
            for each p connected to k {
                if (dis[p]>dis[k]+w[k][p]) {
                    dis[p]=dis[k]+w[k][p]
                    修改H中p的dis
                }
            }
        }
    }

    不难发现,普通二叉堆对这样的问题不能完美解决,相比之下,zkw线段树就自然的多了。直接给出一段代码:

    void dijkstra(int from) {
      memset(dis, 127 / 3, sizeof(dis));
      dis[from] = 0;
      tree.modify(from, 0);
      for (int i = 2; i <= n; i++) {
        int kn = tree.top_pos();
        dis[kn] = tree.pop();
        if (kn == T) {
          cout << dis[T] << endl;
          return;
        }
        for (int k = head[kn]; k; k = graph[k].next) {
          if (dis[graph[k].to] > dis[kn] + graph[k].dis) {
            dis[graph[k].to] = dis[kn] + graph[k].dis;
            tree.modify(graph[k].to, dis[graph[k].to]);
          }
        }
      }
      cout << dis[T] << endl;
    }

    几乎是完全按照伪代码写出。

    效率分析

    以前写zkw线段树的博文已经分析过,zkw线段树的修改复杂度为Θ(lgN)。在这里的程序中,我们需要证明内层的循环次数不会超过O(E),从而证明zkw优化dijkstra的效率为O(NlgE)。显然,在没有负权值的图中,一条边不可能有两次被松弛。聚合分析一下,内层for循环总共至多进行O(E)次,总效率为O(NlgE)+Θ(N)=O(NlgE)。考虑到EN2,有O(lgE)=O(lgN2)=O(lgN),则复杂度可以重写为:O(NlgN)

    实例程序

    tyvj 热浪一题,zkw优化dijkstra标程:

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <queue>
    using namespace std;
    
    template < typename T > struct ZkwHeapNode {
      T value;
      size_t mark;
        ZkwHeapNode() {
      } ZkwHeapNode(const T & _value):value(_value) {
      }
    };
    
    template < typename T, typename Comp > class ZkwHeap {
    
    private:
      typedef ZkwHeapNode < T > Node;
      typedef ZkwHeap < T, Comp > Heap;
    
      Comp cmp;
      Node *NodeList;
      size_t n;
      int init_value;
    
      void fix(size_t _pos) {
        if (cmp(NodeList[_pos << 1].value, NodeList[(_pos << 1) + 1].value))
          NodeList[_pos] = NodeList[(_pos << 1) + 1];
        else
          NodeList[_pos] = NodeList[_pos << 1];
      }
    
    public:
    
      ZkwHeap(const size_t & _MaxN, const T & _init_value):init_value(_init_value) {
        n = 1 << (1 + (size_t) (log(_MaxN) / log(2.0)));
        NodeList = new Node[n << 1];
        for (size_t i = 1; i <= n + n - 1; i++)
          NodeList[i].value = init_value;
        for (size_t i = n; i <= n + n - 1; i++)
          NodeList[i].mark = i - n + 1;
      }
    
      ~ZkwHeap() {
        delete[]NodeList;
      }
    
      T top() {
        return NodeList[1].value;
      }
    
      T top_pos() {
        return NodeList[1].mark;
      }
    
      void modify(size_t _position, const T & _new_value) {
        int _pos = _position + n - 1;
        NodeList[_pos].value = _new_value;
        while (_pos)
          fix(_pos >>= 1);
      }
    
      T pop() {
        T return_value = NodeList[1].value;
        modify(NodeList[1].mark, init_value);
        return return_value;
      }
    };
     const int maxPoint = 2505;
    const int maxEdge = 70005;
    
    struct p {
      int to, dis, next;
    } graph[maxEdge];
    int head[maxPoint];
    int pointer = 0;
    int dis[maxPoint];
    
    int n, m, S, T;
    int zkn;
    ZkwHeap < int, greater < int >>tree(maxPoint, 100000000);
    
    void init() {
      memset(head, 0, sizeof(head));
      memset(dis, 127 / 3, sizeof(head));
    }
    
    void push_edge(int from, int to, int dis) {
      graph[++pointer].to = to;
      graph[pointer].dis = dis;
      graph[pointer].next = head[from];
      head[from] = pointer;
    }
    
    void dijkstra(int from) {
      memset(dis, 127 / 3, sizeof(dis));
      dis[from] = 0;
      tree.modify(from, 0);
      for (int i = 2; i <= n; i++) {
        int kn = tree.top_pos();
        dis[kn] = tree.pop();
        if (kn == T) {
          cout << dis[T] << endl;
          return;
        }
        for (int k = head[kn]; k; k = graph[k].next) {
          if (dis[graph[k].to] > dis[kn] + graph[k].dis) {
            dis[graph[k].to] = dis[kn] + graph[k].dis;
            tree.modify(graph[k].to, dis[graph[k].to]);
          }
        }
      }
      cout << dis[T] << endl;
    }
    
    int main() {
      init();
      cin >> n >> m >> S >> T;
      zkn = (1 << (int)((int)log(n) / log(2) + 1));
      int from, to, dis;
      for (int i = 1; i <= m; i++) {
        cin >> from >> to >> dis;
        push_edge(from, to, dis);
        push_edge(to, from, dis);
      }
      dijkstra(S);
      return 0;
    }
  • 相关阅读:
    HashMap
    java反射
    arraylist和linkedlist区别
    int和Integer的区别
    java 数组排序并去重
    矩阵链乘法问题
    找零问题
    硬币收集问题
    最大借书量问题
    钢条切割问题
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684387.html
Copyright © 2011-2022 走看看