贪心算法-Huffman树

贪心算法-Huffman树

1. 哈并果子问题的概述及案例

    https://www.acwing.com/problem/content/150/

img

    上图为本问题的案例。
    实际上,本题就是霍夫曼树的应用。关于霍夫曼树的定义,这里就不再赘述。

img

    根据上图,实际上这道题就是在询问:将所有的果子堆进行合并,构造成一颗树之后(最终合并为一堆之后),如何使得总消耗(带权路径之和)最小?很明显,哈夫曼树就可以保证带权路径之和最小。我们只需要将这些果子堆构成一颗哈夫曼树即可。

2. 合并果子问题的步骤

    1.  每次从容器中选择两个最小权值的堆,进行合并。将合并之后的堆放回到容器中。
    2.  重复上述过程,直到容器中只剩一堆为止。
    很明显,上述的过程实际上就是在构造哈夫曼树的过程。

3. 合并果子问题的证明

img

    这道题的证明,本质上就是对哈夫曼树进行证明。
        哈夫曼树的特点就是:每次选取最小的两个点进行合并,这样所得到的一定是最优解。因此,最小的两个点一定深度最深。
        1.  现在我们证明:最小的两个点,深度一定最深且可以互为兄弟。我们在这里采用反证法:最小的两个点深度不是最深。假设,a,f点是最小的两个点。a点位于最后一层(n),f点位于n-1层。(具体树的形状见上图)。当我们将f点与b点进行替换之后,权值一定降低。因此,当最小的两个点深度不是最深时,一定不是最优解。因此,最小的两个点,深度一定最深且可以互为兄弟。
        2.  当我们将n个点进行合并时,我们一定会将权值最小的两个点进行合并。此时还剩n-1个点。我们将从n个点合并成为1个点的某个具体方案记为f(n)。为了求出最优方案,显然每一个f(n)在第一步的时候,都需要将两个权值最小的点进行合并。因此:f(n) = f(n-1) + a + b;(其中,a和b是n个点中权值最小的两个点)。由于,每一个f(n)都会有一个固定常数a+b。因此在求出最佳方案时,我们可以将每一个f(n)后面的a+b去掉。若如此做,并不会影响最佳方案的求解。因此,我们将f(n)的计算变成了f(n-1)的计算。
        3.  对于n-1个点,我们也可以采用上述的方式进行计算。这样就由:f(n-1)的计算转换成了f(n-2)的计算。
        4.  一直进行下去,当从2个点合并成为1个点之后,所得到的解就是全局最优解。

4. 合并果子问题的代码

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;

int n;

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



int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int a;
        scanf("%d",&a);
        heap.push(a);
    }
    int res = 0;
    while(heap.size() > 1){
        int a = heap.top();
        heap.pop();
        int b = heap.top();
        heap.pop();
        res += (a+b);
        heap.push(a+b);
    }
    printf("%d",res);
    return 0;
}
    作者:gao79138
    链接:https://www.acwing.com/
    来源:本博客中的截图、代码模板及题目地址均来自于Acwing。其余内容均为作者原创。
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @   夏目^_^  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示