[NOIP2004 提高组]合并果子(贪心,优先队列,小根堆一口气学会)

本文主要是介绍[NOIP2004 提高组]合并果子(贪心,优先队列,小根堆一口气学会),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述
题目链接洛谷P1090

题意

意思就是给你一堆数(比如1 2 3)
让你加到只剩一个数(1+2+3=6)
每次加的代价是加出来的数(1+2=3 , 代价是3 ; 3+3=6 , 代价是6 ; 6+3为总代价)
要求最小代价(引入一种思想:贪心)

贪心

贪心算法,其实根本不用讲,它的核心思路就是求出局部最优解,走一步算一步,每一步都选当前的最优解,根本不从整体上考虑,然后把所有局部解求出来合成整体解。
举个例子,小红邀请小明和小军到小红家玩,但是小红家比较小,只能容纳两人在这里插入图片描述
从图上可以看出,小军无论怎么走都要走2km,然而小明有两条路可以走,一条是1km,一条是3km,如果你是小明,你会怎么走呢,很明显,走1km这条路,就可以愉快地跟小红一起玩耍了。
这就是贪心,选择局部的最优解,然后凑成整体的最优解,如果还不能理解
在这里插入图片描述
那么这次你会作何选择呢,当然是一直往前,而不是弯着走,但是你思考的时候,其实有把它们分开看,也就是,小明和小红家之间有一个必经中转点,所以要到达小红家就得先到必经中转点,到达必经中转点的最优路径是1,而必经中转点到小红家的最优路径也是1,所以这么走最快
你会发现,最终的答案是由局部得来的,而且依赖于前面的选择(不理解可以想想在小明到必经中转点那条2km的路径上再加一个点,然后把这个新点到达必经中转点的路径删掉,这样就到不了小红家了,所以它依赖于前面的选择),这就是贪心

回归题目

讲了这么多,贪心应该懂了吧,现在回归题目,怎么求最小代价呢,那就分成局部问题吧,局部问题就是合成两堆果子的最小代价对吧,而合成两堆果子的最小代价很显然求,就是把这一大堆果子里把最小的两堆果子合在一起吧?
那解法就昭然若揭了,我们把这堆果子放进一个数据结构里,然后对它从小到大排序,每次取出最小的两个出来,加起来,放到最后,然后再排序一次,让这个新合成的果子找到自己的位置,循环往复,直到只有一堆,合成的过程记录一下代价就好了。

但是每次都排序这样的时间复杂度很大,即使是STL里的sort,每次排序也要 O(nlogn) 的时间,那么有什么数据结构能很快地完成排序这个重要的任务呢

堆是一种特殊的二叉树,它分为小根堆,大根堆
树之所以叫树呢,是因为它长这样
在这里插入图片描述
你也可以这么看它
在这里插入图片描述
第一个"爸爸"结点呢,就叫根结点,每个结点有几个儿子就叫几叉树,但是按照爸爸的数值比儿子小这种存储方式的树,叫小根堆爸爸存的数值比儿子大这种存储方式的树,叫做大根堆
也就是说,在大根堆里,最大的数,是根节点,在小根堆里,最小的数是根节点
我们一般用数组来模拟树,比如 tree[100] tree[1]为根结点,tree[2]和tree[3]为1的子结点,也就是,一个结点的儿子在数组中的位置为i*2i*2+1这样就不会有重复,而且能很快查询父亲或者儿子了

用小根堆的代码如下,注解很多,如果不要注解,可以往下翻

有注解版

#include<bits/stdc++.h>
#define op <		//将此处的<号改成>号即为大根堆 
using namespace std; 
int heap[1000086]/*堆*/, size/*堆的长度*/;
void swim(int n)
{//接下来这段要熟悉for循环的执行顺序//for ( 循环开始时执行且只执行一次 ; 1  ; 3 ) {  2  } for (int i=n ; i>1/*不是根节点就上浮*/&&heap[i] op heap[i / 2]/*跟父结点比较*/ ; i /= 2)swap(heap[i], heap[i / 2]);
}//将子节点与父节点进行比较,不断上浮 
int son(int n)//如果左儿子比右儿子小,返回左儿子,不然返回右儿子 
{//此处的处理有点巧妙,本来是要比较左儿子与右儿子的大小//但是因为n的左儿子是2*n,右儿子是2*n+1//所以用一个判断式来代表这个1存不存在//下面的2*n是左右儿子的公有部分 //如果你觉得这样很复杂,可以重新写一个思路一样的更简单的,但是更长的代码 return n

这篇关于[NOIP2004 提高组]合并果子(贪心,优先队列,小根堆一口气学会)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/498379

相关文章

Java中常见队列举例详解(非线程安全)

《Java中常见队列举例详解(非线程安全)》队列用于模拟队列这种数据结构,队列通常是指先进先出的容器,:本文主要介绍Java中常见队列(非线程安全)的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一.队列定义 二.常见接口 三.常见实现类3.1 ArrayDeque3.1.1 实现原理3.1.2

C++ RabbitMq消息队列组件详解

《C++RabbitMq消息队列组件详解》:本文主要介绍C++RabbitMq消息队列组件的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. RabbitMq介绍2. 安装RabbitMQ3. 安装 RabbitMQ 的 C++客户端库4. A

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)

Python中合并列表(list)的六种方法小结

《Python中合并列表(list)的六种方法小结》本文主要介绍了Python中合并列表(list)的六种方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录一、直接用 + 合并列表二、用 extend() js方法三、用 zip() 函数交叉合并四、用

golang实现延迟队列(delay queue)的两种实现

《golang实现延迟队列(delayqueue)的两种实现》本文主要介绍了golang实现延迟队列(delayqueue)的两种实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录1 延迟队列:邮件提醒、订单自动取消2 实现2.1 simplChina编程e简单版:go自带的time

利用Python实现Excel文件智能合并工具

《利用Python实现Excel文件智能合并工具》有时候,我们需要将多个Excel文件按照特定顺序合并成一个文件,这样可以更方便地进行后续的数据处理和分析,下面我们看看如何使用Python实现Exce... 目录运行结果为什么需要这个工具技术实现工具的核心功能代码解析使用示例工具优化与扩展有时候,我们需要将

Python实现获取带合并单元格的表格数据

《Python实现获取带合并单元格的表格数据》由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,所以本文我们就来聊聊如何使用Python实现获取带合并单元格的表格数据吧... 由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,现将将封装成类,并通过调用list_exc

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

SpringKafka错误处理(重试机制与死信队列)

《SpringKafka错误处理(重试机制与死信队列)》SpringKafka提供了全面的错误处理机制,通过灵活的重试策略和死信队列处理,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、Spring Kafka错误处理基础二、配置重试机制三、死信队列实现四、特定异常的处理策略五