zoukankan      html  css  js  c++  java
  • 算法模板:堆,最小生成树(Prim,Kruskal),快速幂

      • 堆一般以二叉堆的形式存在,也可以说是一个“优先队列”,堆中数据以一定顺序存放,比如小根堆(根节点小于子节点)

      • 堆涉及到的基本操作:

        • 上浮:新插入元素时在底,上浮到相应的位置
        • 下沉:删除元素时,删除最小元素(根节点),以堆底元素作为临时的root,然后进行下沉调整堆
      • 基本模板:

        • 堆结构:一个数组,heap[0]来存储堆中元素个数,heap[heap[0]]是堆底元素,heap[heap[0]/2]是堆底元素的父节点
        int heap[MAXN];
        
        • 上浮:
        void swim(int x) {
            int p = x >> 1;
            while(p>0 && heap[x]<heap[p]) {
                swap(heap[x],heap[p]);//子节点<父节点,则互换位置
                x = p;
                p = x >> 1;  //继续上浮
            }
        }
        
        • 下沉:
        void sink(int x) {
            int c = x << 1;
            while(c <= heap[0]) {
                if(c+1<=heap[0] && heap[c+1]<heap[c]) c++;
                if(heap[c] < heap[x]) {
                    swap(heap[c],heap[x]);
                    x = c;
                    c = x << 1;
                } else {
                    break;
                }
            }
        }
        
        • 插入:
        void insert(int x) {
           	heap[0] ++;
            heap[heap[0]] = x;
            swim(heap[0]);
        }
        
        • 删除
        void pop() {
            swap(heap[1],heap[heap[0]--]);
            sink(1);
        }
        
    1. 最小生成树

      • Prim && Kruskal :

        • Prim: 以连接节点的临边找最小值

        • Kruskal: 直接排序边

        • 稠密图用Prim,稀疏图用Kruskal

      • Prim :

        • 1、从任意一个顶点开始构造生成树,假设就从1号顶点吧, 首先将顶点1加入生成树中,用一个一维数组vis来标记 哪些顶点已经加入了生成树。
          2、用数组dis记录生成树到各个顶点的距离,最初生成树中之后1号 顶点,有直连边时,数组dis中存储的就是1号顶点到该顶点 的边的权值,没有直连边的时候就是无穷大,即初始化dis数组。
          3、从数组dis中选出离生成树最近的顶点(假设这个顶点为j) 加入到生成树中(即在数组dis中找到最小值)。再以j为中间点, 更新生成树到每一个非树顶点的距离(就是松弛啦), 即如果dis[k]>e[j][k]则更新dis[k]=e[j][k]。
          4、重复第三步,直到生成树中有n个顶点为止。

        • 以链式前向星存图

          struct Edge
          {
              int val,prev,next;
          }e[MAXN];
          
        • 添加一条边

          void add(int p,int n,int v) {
              e[++k].prev = first[p];
              first[p] = k;
              e[k].next = n;
              e[k].val = v;
          }
          
        •   priority_queue<pair<int,int> > q;
            
            int prim() {
                q.push(make_pair(0,1));
                while(!q.empty() && cnt<n) {
                    int now = q.top().second;
                    int v = q.top().first;
                    q.pop();
                    if(vis[now]) continue;
                    vis[now] = 1;
                    ans += v;
                    cnt ++;
                    for(int i=first[now]; i; i=e[i].prev) {
                        if(!vis[e[i].next]) 
                            q.push(make_pair(-e[i].val,e[i].next));  //优先队列默认大根堆
                    }
                }
                return -ans;
            }
          
      • Kruskal:

        • 主要思路:kruskal就是基于并查集的贪心算法
          1. 输入边
          2. 结构体排序,以小到大排边
          3. 建并查集,联通树
        • 存储边
        struct edge {
            int val, prev, next;
        } e[MAXN];
        
        bool cmp(const edge& a, const edge& b) { return a.val < b.val; }
        
        • 并查集
        int pre[MAXN];
        int find(int x) {
            while (x != pre[x]) {
                x = pre[x] = pre[pre[x]];
            }
            return x;
        }
        
        • kruskal:
        void kruskal() {
            for (int i = 1; i <= m; i++) {
                int fx = find(e[i].prev);
                int fy = find(e[i].next);
                if (fx != fy) {
                    ans += e[i].val;
                    cnt++;
                    pre[fx] = fy;
                } else {
                    continue;
                }
                if (cnt == n-1) break; 
            }
        }
        
    2. 快速幂 || 快速幂取余

      • 每一步把指数折半,而相应的底数平方,从而减少循环次数

        eg. 3^10 == (3x3)^5 == 9 x (9x9)^2 == 9 x 81 x 81 x 81

      • 取余公式:

        (a + b) % p = (a % p + b % p) % p
        (a - b) % p = (a % p - b % p) % p
        (a * b) % p = (a % p * b % p) % p
        
      • 模板:

        LL quickPower(LL base,LL power,LL modk) {
            LL ans = 1;
            while(power > 0) {
                if(power & 1) { //奇数次乘入结果
                    ans = ans * base % modk;
                }
                power >>= 1; //power折半
                base = (base * base) % modk; //底数平方
            }
            return ans%modk;
        }
        
  • 相关阅读:
    6.Docker中上传镜像到docker hub中
    altermanager使用报错
    Grafana官方和社区提供的dashboard
    什么是 云原生?
    prometheus被OOM杀死
    新版GRANAFA K8S插件 K8S NODE 图表不显示问题解决方法
    python2和python3的不同
    一次使用Python连接数据库生成二维码并安装为windows服务的工作任务
    Python连接oracle
    numpy.ndarray的赋值操作
  • 原文地址:https://www.cnblogs.com/syisyuan/p/13708523.html
Copyright © 2011-2022 走看看