luoguP1090ybtoj堆例题1合并果子

Posted SSL_ZZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luoguP1090ybtoj堆例题1合并果子相关的知识,希望对你有一定的参考价值。


Link

luogu传送门

ybtoj传送门

题面

//因为不知道侵不侵权所以就是题面是私密的,有账号的直接看转送门就可了


题目大意

一个序列,每两个数字可以加在一起,消耗能量为这两个数字的和,将序列最后+为一个数字,且能量消耗最少
//xs,和原题长得一点都不像,又好似一模一样


解题思路

堆的基本背景

堆就是树,且是完全二叉树
小根堆,父亲必须比儿子小,这就使得根节点一定是最小的那个
大根堆,父亲必须比儿子大,根节点一定是最大的那个
复杂度:取最大/最小 O ( 1 ) O(1) O(1),维护堆 O ( l g   n ) O(lg\\ n) O(lg n)

堆的常用操作

以下以小根堆为例

  1. 建树:就是将堆初始化处理,每加一个数就用up()维护 //反正我是这么建树的
  2. up():与自己的父亲比较,如果比父亲小,也就是应该在上面,就和父亲交换,不断up()上浮
  3. down():与两个儿子比较,如果应该待在下面,就和儿子交换,不断下沉
  4. 删堆头:将堆头替为堆最后的数,删掉最后一个数,然后down()维护 //这样替保证树中间不会有空(完全二叉树)

堆的大板子题
//没想到有一天还要打这题,真是使不得,而且我还没打过这个板子博客:(

由小学知识可得,越小的数加在一起能量消耗的越少,如果不停sort()一定会爆
小根堆取最小数,复杂度大约 O ( n l g   n ) O(nlg\\ n) O(nlg n)


Code

#include <iostream>
#include <cstdio>

using namespace std;

int n, k;
long long ans, a[40100];

void up(int x) {
	while(x > 1 && a[x / 2] > a[x]) {  //和父亲比较
		a[0] = a[x], a[x] = a[x / 2], a[x / 2] = a[0];
		x = x / 2;
	}
}

void down(int x) {
	int t = x * 2;
	if (t < n && a[t] > a[t + 1]) t++;  //更小的儿子会更有可能交换
	while(x <= n && t <= n && a[x] > a[t]) {
		a[0] = a[x], a[x] = a[t], a[t] = a[0];
		x = t, t = x * 2;
		if (t < n && a[t] > a[t + 1]) t++;
	}
}

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		up(i);  //建树
	}
	for(int i = 1, m = n; i < m; i++) {
		k = a[1], a[1] = a[n], n--, down(1);  //把第一个数提出来,并删堆头
		a[1] += k, ans += a[1], down(1);  //将第二数变为两个最小数的和,down()维护
	}
	printf("%lld", ans);
}

以上是关于luoguP1090ybtoj堆例题1合并果子的主要内容,如果未能解决你的问题,请参考以下文章

luogu 1090 合并果子

洛谷P1090 合并果子

洛谷 1090合并果子

P1090 合并果子

洛谷P1090 合并果子

P1090 合并果子(哈弗曼树)