简单的堆


STL的使用方法

假设有一个 struct, 名为 node, 它的小于号是这样的:

bool operator<(node s1, node s2) {return //balabala;}

然后创建一个 priority_queue, 这样创建: priority_queue<node> Q;

但是 prioity_queue 默认是大根堆, 所以如果想用小根堆, 就要把小于号定义为

bool operator<(node s1, node s2) {return !(//balabala);}

即把 bool 值在外面套个取反。

二叉堆



// bigger top_element heap
int h[N], n;
void up(int p) { for(;p;p>>=1) if(h[p]>h[p>>1])swap(h[p],h[p<<1]); else break; }
void down(int p) {
	for(int q=p<<1; q<=n; p=q,q=p<<1) {
		if(q<n && h[q|1]>h[q]) q|=1;
		if(h[q]>h[p]) swap(h[p],h[q]); else break;
	}
}
void ins(int val) {h[++n]=val; up(n);}
int top() {return h[1];}
void pop() {h[1]=h[n--]; down(1);}

// advanced skill : remove a element of heap with index (not must root)
// because a element can't be "up" while it must be "down",  and can't be "down"
// while it must be "up"
void remove(int k) {
	heap[k] = heap[n--];
	up(k), down(k);
}

//advanced skill : remove a element of heap with value (not must root)
// use lazy deleting, creat another heap, who bigger while this heap is bigger, 
// push elements were wanted to delete, then you can know if the top of the original
// heap must be delteted


// luogu P3378
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int T;
int h[1001000], n;
void up(int p) {for(;p;p>>=1) if(h[p>>1]>h[p])swap(h[p],h[p>>1]); else break; }
void down(int p) {
    for(int q=p<<1; q<=n; p=q, q=p<<1) {
        if(q<n && h[q|1] < h[q]) q|=1;
        if(h[p] > h[q]) swap(h[p], h[q]); else break;
    }
}
void ins(int val) {h[++n]=val; up(n);}
int top() {return h[1];}
void pop() {h[1]=h[n--]; down(1);}

int main(){
	scanf("%d", &T);
	 for(int i=1,a,x;i<=T;++i){
	 	scanf("%d",&x);
	 	switch(x){
	 		case 1:scanf("%d",&a);ins(a);break;
	 		case 2:printf("%d
",top());break;
	 		case 3:pop();break;
		 }
	 }
	 return 0;
}

至于这个怎么存结构体, 挺显然的不用我多说了。

左偏树

待补。

Huffman编码

这部分我是抄的 lydrainbowcat 的 《进阶指南》, 安利一下, 书名全名叫《算法竞赛进阶指南》, 作者是李煜东, 对于提高选手,这书的动态规划和图论部分十分值得一读。

考虑这样一个问题:构造一棵包含 n 个叶节点的 k 叉树, 其中第 i 个叶子节点带有权值 (w_i), 要求最小化 (sum w_i*dep_i), 其中 (dep_i) 表示第 i 个叶子节点到根节点的距离。该问题的解被称为 k 叉 Huffman 树(哈夫曼树)。

为了最小化 (sum w_i*dep_i) 应该让权值较大的叶子节点深度尽量小。

当 k=2 时, 可以用如下算法来求出二叉 Huffman 树:

  1. 建立一个小根堆, 插入这 n 个叶子节点的权值
  2. 从堆中取出最小的两个权值 (w_1)(w_2), 令 (ans += w_1+w_2)
  3. 建立一个权值为 (w_1+w_2) 的树节点 (p), 令 (p) 成为权值为 (w_1)(w_2) 的树节点的父亲
  4. 在堆中插入权值 (w_1+2_2)
  5. 重复第 (2 ext ~4) 步, 直到堆的大小为 1。

最后, 由所有新建的 p 与原来的叶子节点构成的树就是 Huffman 树, 变量 ans 就是 (sum w_i*dep_i) 的最小值。

对于 k > 2 的情况, 如果 "推广" k=2 时的做法, 在进行最后一轮循环时, 如果堆的大小在 ([2,k-1]) 之间, 那么整个 Huffman 树的根的子节点的个数小于 k, 显然不是最优解。

因此, 要人为添加一些权值为 0 的叶子节点, 使得最后叶子节点的总数 n 满足 ((n-1)mod (k-1) = 0), 这就是让子节点不足 k 个的情况发生在最底层, 而不是根节点处。

(对于这个式子的解释, 可以看成要减少 n-1 个节点, 每次减少 k-1 个节点)

原文地址:https://www.cnblogs.com/tztqwq/p/13797319.html