zoukankan      html  css  js  c++  java
  • 51nod 1117 聪明的木匠:哈夫曼树

    题目链接:http://class.51nod.com/Challenge/Problem.html#problemId=1117

    一、题目描述

    一位老木匠需要将一根长的木棒切成N段。每段的长度分别为L1,L2,......,LN(1 <= L1,L2,…,LN <= 1000,且均为整数)个长度单位。
    我们认为切割时仅在整数点处切且没有木材损失。
    木匠发现,每一次切割花费的体力与该木棒的长度成正比,不妨设切割长度为1的木棒花费1单位体力。
    例如:若N=3,L1 = 3,L2 = 4,L3 = 5,则木棒原长为12。
    木匠可以有多种切法,如:
    第一种切法先将12切成3+9.,花费12体力,再将9切成4+5,花费9体力,一共花费21体力;
    第二种切法还可以先将12切成4+8,花费12体力,再将8切成3+5,花费8体力,一共花费20体力。
    显然,后者比前者更省体力。
    那么,木匠至少要花费多少体力才能完成切割任务呢?

    输入描述

      第1行:1个整数N(2 <= N <= 50000)

        第2 - N + 1行:每行1个整数Li(1 <= Li <= 1000)。

    输出描述

      输出最小的体力消耗。

    样例输入

    3
    3
    4
    5

    样例输出

      19

    二、解题思路

    这道题运用了三个主要概念:“正难则反” 和 “哈夫曼树”和“优先队列”

    我们先正着来思考,发现对于最少的体力,应该是最短板被切割的次数最多,因为总的体力花费是每个木棒被切的次数。

    可是我们发现很难实现,所以反过来思考,将n个木棒拼接回原来的样子并且花费体力最少,每次也都是需要将两个最短的木棒拼起来。

    每次从剩余的木棒中取出最短的两个,拼好之后放回去,这就用到了哈夫曼树的性质。

    我们先来复习一下哈夫曼树

    给定N个权值(权值是每个节点上的数值)作为N个叶子结点,构造一棵二叉树。

    哈夫曼树就相当于找到两个权值最小的节点结合在一起,叶子结点是后面没有节点的节点。

     

    以本图为例,节点上的数字,是点的权值Wi。

    在这幅图里,叶子结点(绿色的节点)分别为2、4、7、8、12、19、20、30。

    而整个树的权值等于所有叶子结点的Wi×Li(Li为这个节点到根节点的距离)的和。

    按照本图的分配方法,整棵树的权值为:(2+4)×5+ 7×4 + (8+12+19)×3+(20+30) ×2 = 275。

    构造哈夫曼树,需要配合一个优先队列来实现。

    在这道题里,我们可以借助哈夫曼树来实现我们的思想:

    每次从剩余的木棒中取出最短的两个,拼好之后放回去,这就用到了哈夫曼树的性质。

     

      接下来我们复习一下优先队列

    只要是这么写的,就说明q这个优先队列是从小到大排列的:

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

    现在给优先队列的基本操作(只在本题中使用的基本操作):

    priority_queue <long long, vector <long long>, greater <long long> > que;

             定义一个从小到大排列的优先队列

    que.push(1);            往优先队列que中存入1这个数

    que.size()                 返回que里元素个数

    que.top()                   这个只能在优先队列里使用,不能再普通队列里使用。

              如果是从小到大排列的,第一个肯定是最小的,最后一个是最大的。

             那么返回第一个数字(在这道题里是返回最小的)

    三、代码

    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<iostream>
    using namespace std;
    priority_queue <int, vector<int>, greater<int> > que;
    int n, l, first_small, second_small, ans, tmp;
    
    int main(){
        cin >> n;
        for(int i = 0;i < n;i++){
            cin >> l;
            que.push(l);
        }
        while(que.size() > 1){
            first_small = que.top();//找到在所有木板中最短的 
            que.pop();//把这个木板在所有木板里删除 
            second_small = que.top();//找到在所有木板中第二短的  
            que.pop();//把这个木板在所有木板里删除 
            tmp = first_small + second_small;//这两个木板结合起来成为一个新的木板 
            ans += tmp;//往体力值里加上这个木板的长度 
            que.push(tmp);//往所有木板里放入这个崭新的木板 
        }
        cout << ans << endl;//输出体力值 
        return 0;
    }
  • 相关阅读:
    windows编程学习笔记
    自学JAVA-12:MySQL数据库
    自学JAVA-11:IO流
    自学JAVA-10:集合
    自学JAVA-9:基本类常用方法整理
    自学JAVA-8:异常
    自学JAVA-7:多态
    自学JAVA-6:继承
    自学JAVA-5:修饰符、对象初始化
    自学JAVA-4:方法、对象、类、属性
  • 原文地址:https://www.cnblogs.com/elisa02/p/12905697.html
Copyright © 2011-2022 走看看