今天学了
堆
概念:
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<=K2i+2 ,则称为小堆(或大堆)。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
还没完全学懂,码一下prim算法的堆优化吧
#include<stdio.h>
#include<string.h>
#define Size 10001
const int inf = 0x7f;
int dist[Size];//各点到最小生成树的最小距离
bool book[Size];//标记是否连入最小树
int n, m; //分别是顶点个数,边个数
int count, sum; //生成树中顶点个数,树的路径之和
int h[Size];//一维数组存储堆
int pos[Size];//每个顶点在堆中的位置
int size;//堆的大小
int first[Size], next[Size];
int u[Size], v[Size], w[Size]; //边的两个顶点以及该边的权值,邻接表
void init() { //算法初始化
memset(book, 0, sizeof(book));
memset(first, -1, sizeof(first));
count = 0;
sum = 0;
size = 0;
memset(dist, 0x7f, sizeof(dist));
}
void swap(int x, int y) { //交换堆中的两个元素,x,y分别为堆中元素的标号
int t = h[x];
h[x] = h[y];
h[y] = t; //交换
int tt = pos[h[x]];
pos[h[x]] = pos[h[y]];
pos[h[y]] = tt; //同步更新pos数组
}
void siftdown(int i) { //下滤(向下调整函数),i为向下调整的结点编号
int t, flag = 0; //flag用来标记是否继续向下调整
while (i * 2 <= size && flag == 0) {
if (dist[h[i]] > dist[h[2 * i + 1]]) {//比较i和它左儿子i*2在dist中的值,并用t记录较小的结点的编号
t = i * 2;
} else {
t = i;
}
if (i * 2 + 1 <= size) { //如果它有右儿子,再对右儿子进行讨论
if (dist[h[t]] > dist[h[i * 2 + 1]]) {//右儿子的值更小,更新较小的结点标号
t = i * 2 + 1;
}
}
if (t != i) { //如果发现最小的结点编号不是自己,说明子节点中有比父节点更小的
swap(t, i); //交换它们
i = t; //更新i为刚才与它交换的儿子的结点的编号
} else {
flag = 1; //否则说明当前的父节点已经比两个子节点都要小了,不需要在进行调整了
}
}
}
void siftup(int i) {
int flag = 0; //flag用来标记是否继续向上调整
if (i != 1) {//如果是堆顶就返回,不需要调整,
while (i != 1 && flag == 0) { //不在堆顶,且当前结点i的值比父节点小的时候继续向上调整
if (dist[h[i]] < dist[h[i / 2]]) { //判断是否比父节点小
swap(i, i / 2); //交换它和它父节点的位置
} else {
flag = 1; //不需要再次调整
}
i = i / 2; //这句话很重要,更新编号i为它父节点的编号,以便于下一次继续向上调整
}
}
}
int pop() {//从堆顶取出一个元素
int t = h[1]; //记录堆顶点的值
pos[t] = 0;
h[1] = h[size]; //将堆的最后一个顶点赋值到堆顶
pos[h[1]] = 1;
size--;//堆的元素减少1
siftdown(1);//将堆顶的元素向下调整
return t;//返回记录的堆顶点的值
}
void run_prim() {
book[1] = true;
count++;//源点入树
dist[1] = 0;
int k = first[1];
while (k != -1) {
dist[v[k]] = w[k];
k = next[k];
}//初始化源点到其余各个顶点的初始距离
size = n;
for (int i = 1; i <= size; i++) {
h[i] = i;
pos[i] = i;
}
for (int i = size / 2; i >= 1; i--) {
siftdown(i);
}//初始化堆
pop();//先弹出一个堆顶元素,因为此时堆顶是1号元素
while (count < n) {
int j = pop();
book[j] = 1;
count++;
sum += dist[j];
//扫描当前j顶点所有边再以j为中间顶点,进行松弛
k = first[j];
while (k != -1) {
if (book[v[k]] == 0 && dist[v[k]] > w[k]) {
dist[v[k]] = w[k];//更新距离
siftup(pos[v[k]]);//堆该点在堆中进行向上调整
}
k = next[k];
}
}
}
int main() {
scanf("%d %d", &n, &m);
init();
for (int i = 0; i < m; i++) { //开始读入边
scanf("%d %d %d", &u[i], &v[i], &w[i]);
}
for (int i = m + 1; i <= 2 * m; i++) { //因为是无向图,存储两次
u[i] = v[i - m];
v[i] = u[i - m];
w[i] = w[i - m];
}
for (int i = 1; i <= 2 * m; i++) { //开始使用邻接表存储边
next[i] = first[u[i]];
first[u[i]] = i;
}
run_prim();
printf("%d", sum);
}
刷题:
租用游艇: