• NOIP 2004 合并果子


    洛谷P1090

    https://www.luogu.org/problemnew/show/P1090

    JDOJ 1270

    题目描述

    在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

    每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1n1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

    因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

    例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15。可以证明 1515 为最小的体力耗费值。

    输入输出格式

    输入格式:

    共两行。
    第一行是一个整数 n(1leq nleq 10000)n(1n10000) ,表示果子的种类数。

    第二行包含 nn 个整数,用空格分隔,第 ii 个整数 a_i(1leq a_ileq 20000)ai(1ai20000) 是第 ii 种果子的数目。

    输出格式:

    一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^{31}231 。

    输入输出样例

    输入样例#1: 复制
    3 
    1 2 9 
    
    输出样例#1: 复制
    15
    

    说明

    对于30%的数据,保证有n le 1000n1000:

    对于50%的数据,保证有n le 5000n5000;

    对于全部的数据,保证有n le 10000n10000。

    看了很多题解,觉得优先队列的解法是最符合弱者心理贪心内涵和解题思路的。但是并没有对优先队列进行介绍,直接刚代码,对新手和对STL不了解的童鞋很不友好。


    福利来了!

    STL中的优先队列的相关知识概览:

    首先要明白,#include<queue>头文件里不仅仅有循环队列queue这唯一的容器可以使用,还有一个高级的priority_queue(也就是优先队列)可以供选手们A掉一些思路比较线性的“伪难题”。

    首先是声明

    priority_queue<int> q;

    <>里是什么都可以塞进去的

    还有一个比较高级的,C++内置二元组,声明方法如下:

    priority_queue< pair<int,int> > q;

    pair<>里是内置二元组,尖括号里分别指定二元组的第一元,第二元的类型。可以比较大小,以第一元为第一关键字,第二元为第二关键字。

    什么是优先队列

    优先队列可以理解成一个大根二叉堆(又来了)。

    好吧我先解释一下啥是“大根二叉堆”。

    大根二叉堆

    虽然这个名字有点污,但是这是一个很有用的数据结构,首先是二叉堆。它是一种支持插入删除和查询最值的数据结构。本质上是一棵满足“堆”的性质的完全二叉树。

    完全二叉树

    二叉树的性质不多说了。一个h层的二叉树,如果它是完全二叉树,它的前h-1层都是满的,而且最后一层的所有叶子节点(就是绝育的节点)都从左至右依次排列,那就是一棵完全二叉树。

    堆的基本性质其实就是一棵完全二叉树,一般把堆分为3种:大根堆,小根堆和普通堆。 大根堆的定义:如果树中任意一个节点的权值都小于等于其父节点的权值,则成为大根堆。(通俗的说,就是越往上越大的堆) 反之则为小根堆。 普通堆么,顾名思义就是啥也不是的堆。

    基本调用方式

    插入:

    q.push(x);//插入堆

    删除:

    q.pop();//删除堆顶元素

    查询:

    int x=q.top();//查询堆顶元素(最大值)

    这里注意一下时间复杂度,插入和删除是O(logn)的,查询是O(1)的。

    优先队列实现小根堆

    考虑到重载运算符对于新手来说不是很友好,在这里介绍一个比较取巧的方法: 不是说优先队列是大根堆么?好鸭! 我把要插入元素的相反数放入堆中,拿出来的时候取反即可: 比如插入1,2,3,改为插入-1,-2,-3,此时-1最大,把-1取出的时候变为-(-1),相当于取出了最小的1。


    本题根本不需要我上述的那些繁繁琐琐的内容,但是既然用了优先队列,就要把优先队列是怎么回事搞明白,搞清楚,这样也会对相关的数据结构有一个更清楚的认识,以后再用也会思路更清晰。

    Code:

    #include<cstdio>
    #include<queue>
    #include<vector>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int ans;
    priority_queue <int,vector<int>,greater<int> >apple;
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int a;
            scanf("%d",&a);
            apple.push(a);
        }
        while(apple.size()>1)
        {
            int x=apple.top();apple.pop();
            int y=apple.top();apple.pop();
            apple.push(x+y);
            ans=ans+x+y;
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    因打卡界面更新,稍修改主要代码【2021/1/16】
    Python爬虫:使用Selenium爬取指定上市公司(如浦发银行)的今年公告信息
    Python爬虫:爬取福州链家的不同区域的二手房成交信息
    蓝精灵小组第十三周学习总结
    2020-2021-1学期 20202417《网络空间安全专业导论》第十三周学习总结
    蓝精灵小组第十二周学习总结
    蓝精灵小组第十一周学习总结
    2020-2021-1学期 20202417《网络空间安全专业导论》第十二周学习总结
    2020-2021-1学期 20202417《网络空间安全专业导论》第十一周学习总结
    2020-2021-1学期 20202417《网络空间安全专业导论》第十周学习总结
  • 原文地址:https://www.cnblogs.com/fusiwei/p/11166371.html
Copyright © 2020-2023  润新知