合并果子(贪心)


题目:合并果子(贪心)

题意:

n堆果子,每次合并可以选择任意两堆合并,每次合并的消耗的体力是这两堆果子的重量和,求把n堆果子合并成1堆所需消耗体力的最小值。

输入格式

输入包括两行,第一行是一个整数 n,表示果子的种类数。

第二行包含 n 个整数,用空格分隔,第 i 个整数 ai 是第 i 种果子的数目。

输出格式

输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。

输入数据保证这个值小于 2^31

数据范围

1≤n≤10000,
1≤ai≤20000

输入样例:

3

1 2 9

输出样例:

15

样例解释:

第一次合并选择1和2,消耗体力3,第二次合并选择3和9,消耗体力12。总共消耗的体力为3 + 12 = 15。

题目分析:贪心。

解题步骤:

  1. 每次选择序列中最小的两个元素进行合并,序列中添加一个新的元素,即这两个元素的和。
  2. 重复步骤1,直到序列中的元素为1个。

贪心策略证明:

如下图,如果把合并的过程看成一棵树,叶子节点为要合并的果子的数量。可以知道,越先合并的果子的深度越大,权重也会越大。例如下图中,a和b在合并的时候会被加1次,(a + b)和c在合并的时候会被加1次,所以a加了两次,b加了两次,c加了一次。

  1. 设贪心得出的答案是cnt,本题的正确答案是ans。
  2. 由于本题的答案ans是所有合并方案的最小值,所以必有ans <= cnt。
  3. h(x)x在树中的深度,设在正确答案中有c < ah(c) < h(a),则我们可以交换ac的合并顺序,交换不影响其他点的权重。交换后,数值小的点权重变大,数值大的点权重变小,且a和c的权重改变量是相等的,所以交换后能够得到更小的答案。故有cnt <= ans。
  4. 由于ans <= cntcnt <= ans,得cnt = ans。

                      

 

 

AC代码:

#include<iostream>

#include<queue>

 

using namespace std;

 

void solve(){

    int n;

    scanf("%d", &n);

    

    priority_queue<int, vector<int>, greater<int> > pq;

    while(n--){

        int x;

        scanf("%d", &x);

        pq.push(x);

    }

    

    int res = 0;

    while(pq.size() > 1){

        int x = pq.top(); pq.pop();

        int y = pq.top(); pq.pop();

        

        res += x + y;

        pq.push(x + y);

    }

    printf("%d\n", res);

}

 

int main(){

    solve();

    

    return 0;

}

时间复杂度:O(NlogN)。

空间复杂度:O(N)。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM