首页 > 其他分享 >acwing148. 合并果子

acwing148. 合并果子

时间:2022-09-26 11:46:44浏览次数:84  
标签:果子 int 交换 合并 最小 heap 代价 acwing148

acwing148. 合并果子

原题链接:https://www.acwing.com/problem/content/150/

思路

贪心使用集合角度思考,需要去证明最优解在某个集合的子集之中,其他的子集就可以摒弃掉。

这个题使用贪心来考虑。


我们可以发现,每次合并两堆,然后形成一个新的堆,再去找两堆合并...反复。这个合并的过程就像一颗二叉树。

比如样例,先合并1和2,再合并3和9,最后总代价就是3+12 = 15,这个合并过程就是一颗二叉树。

其实总代价最小取决于每个叶子结点到达根节点的距离。
1 * 2 + 2 * 2 + 1 * 9 = 15,所以总的代价是和树的形态有关的,最小的代价就是huffman树(带权最优树)


接下来使用贪心来解这道题。

猜想:每两次合并最小的两堆,最后的总代价最小

看下图的集合,接下来去证明最优解在左边这个子集中

证明完最优解在左边的子集之后,就可以第一步先合并两个最小的,然后递归去做就可以了。

假设下图是"其他"这个子集合并的情况

如果最小的两个在同一层,最小的这两个是a、c,那么如果交换b和c进行交换总代价是不变的(b和c到达根节点的距离没有改变),所以交换之后就是合并了两个最小的,"合并两个最小的"是最优解,虽然这个其他情况这个时候(最小两个在同一层)中存在最小的代价,但是可以转换成合并两个最小(b和c交换),因此最小代价是在左边子集的,最优解是在左边的子集中的

如果两个最小的没有在同一层。比如两个最小的是a、d,那么将d和b交换。

交换前(其他叶子结点a、c等等到达根节点距离没变,代价都不变,所以只看d和b的代价变化)代价是 
=> d * L1 + b * L2 
交换之后代价变成了 
=> b * L1 + d * L2 。

交换前的代价-交换后的代价:
L1 < L2 ,d < b
L1 * (d-b) + L2 * (b-d) = (L1-L2) * (d-b) > 0

交换前的代价比交换后的代价大,所以交换进行之后代价变小,因此这个"其他"子集的代价更大,最小代价是在左边子集的,最优解是在左边的子集中的。

"最优解是在左边的子集中的"得证。

那我们要维护一个哈夫曼树,其实只需要一个小根堆就可以了(可以取最小值,删除最小值,插入值)。最后时间复杂度为\(O(nlogn)\)(小根堆存取操作是logn,要进行n次,所以nlogn)

使用小根堆,每次取出两个最小的,将其合并,记录代价,再存入小根堆。

代码

#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    
    priority_queue<int,vector<int>,greater<int>> heap; // 小根堆
    while(n --)
    {
        int x;
        scanf("%d",&x);
        heap.push(x);
    }
    
    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;
}

标签:果子,int,交换,合并,最小,heap,代价,acwing148
From: https://www.cnblogs.com/rdisheng/p/16730322.html

相关文章

  • EasyExcel单元格合并策略
    EasyExcel单元格合并策略自定义策略packagecn.most.rsgzglxt.utils;importcom.alibaba.excel.metadata.Head;importcom.alibaba.excel.metadata.data.CellData;im......
  • SDN第一次实验报告(第1、2次合并)
    第一次实验报告一、实验目的能够使用源码安装Mininet;能够使用Mininet的可视化工具生成拓扑;能够使用Mininet的命令行生成特定拓扑;能够使用Mininet交互界面管理SDN拓......
  • Git 合并代码遇到冲突如何解决
    Git合并代码遇到冲突如何解决根据这个视频记录的笔记【git合并代码遇到冲突如何解决】https://www.bilibili.com/video/BV1hb4y1e7p9?share_source=copy_web背景实......
  • 合并石子
    这个就强调一点:一定要分清是线性排列还是环形排列,如果是环形的话,只需要将n+1--2n重新赋一遍值,但是:!!!s[i]要继续s[i]=s[i-1]+a[i],而且别忘了给f[n+1][n+1]---f[2n][......
  • 实际工作中 GIT 如何创建合并推送分支
    实际工作中GIT的分支合并是如何操作的?根据这个视频记录的笔记【实际工作中GIT如何创建合并推送分支】https://www.bilibili.com/video/BV1eD4y1F7Kt?share_source=cop......
  • 力扣23(java)-合并k个升序链表(困难)
    题目:给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。 示例1:输入:lists=[[1,4,5],[1,3,4],[2,6]]输出:[1,1,2,3,4......
  • 环形合并石子
    #include<iostream>#include<cstring>intf[1000][1000],s[10000],a[10000];intk,n,maxn,miny=100000000;usingnamespacestd;intmain(){cin>>n;mems......
  • 合并两个顺序表
    合并两个顺序表已知有两个顺序表LA和LB(代码如下所示),并且两个表的储存的两个相邻元素,后者要大于或等于前者。合并这两个表。LA,LB的初始化#include<stdio.h>#defineMaxS......
  • 88. 合并两个有序数组
    题目给你两个按非递减顺序排列的整数数组 nums1和nums2,另有两个整数m和n,分别表示nums1和nums2中的元素数目。请你合并nums2到nums1中,使合并后的数组同......
  • m3u8文件后缀jpg,png等处理方法及视频合并
    处理#解析伪装成png的tsdefresolve_ts(src_path,dst_path):'''如果m3u8返回的ts文件地址为https://p1.eckwai.com/ufile/adsocial/7ead0935-dd4f-4d2......