【笔记】贪心(2)---哈夫曼树、排序不等式、绝对值不等式、推公式类


贪心问题

贪心问题很多都需要证明,但题目背后往往是那几个经典的类型。笔记用于积累,越做越有。


一、哈夫曼树

哈夫曼树的构造

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树。

哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。哈夫曼树是贪心算法的经典例子:总是从森林中选取两颗根节点权值最小的二叉树,作为一棵树新的左右子树,新二叉树的根节点的权值为左右子树根节点权值之和。从森林中删除原二叉树,并将新树加入到森林中。重复此过程直到森林中只有一颗二叉树。

算法实现

使用小根堆(优先队列)实现,这样总能选取到根节点权值最小的两颗二叉树。堆中存储的对象是权值。

#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\n",res);
    

    return 0;
}

二、排序不等式

排序不等式分析

排队打水问题是经典的排序不等式问题:
要保证打水的总等待时间最短,则打水时间较短的人先打水。
类比短进程优先调度算法( SJF )可使进程的平均等待时间最短。

算法实现

思路1:打水时间最短的被等待的次数最多。
思路2:打水时间最长的要等待之前所有打水完成之后才开始。

#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N=100010;
int n,a[N];

int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    
    sort(a,a+n);
    
    LL res=0;
    for(int i=n-1,j=0;i>=0;i--,j++)
        res+=j*a[i];
            
    cout<<res<<endl;
    return 0;
}

绝对值不等式问题

绝对值不等式分析

货仓选址问题是经典的绝对值不等式问题:
对于两个点: ∣ x − a ∣ + ∣ x − b ∣ > = ∣ a − b ∣ |x-a|+|x-b|>=|a-b| xa+xb>=ab等号成立的条件是 x 位于两个点之间(包括端点)。

对于 n 个点则分组考虑:货仓在x坐标处,x 左侧的商店有 P 家,x 右侧的商店有 Q 家。当P == Q时为最优解(总是位于左右两个点之间)。n 为奇数时,x 在n个点的中位数上为最佳位置;n 为偶数时,x 最中间两个点之间任意位置。

该问题还可以推广到 2 维、3 维(三分)、n 维(模拟退火)等。

算法实现

#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int n,a[N];

int main(){
    cin>>n;
    for(int i=0;i<n;i++)    cin>>a[i];
    sort(a,a+n);
    int res=0;
    for(int i=0;i<n;i++)
        res+=abs(a[i]-a[n/2]);
    cout<<res<<endl;
    return 0;
}

推公式类问题

此类问题需要结合具体题目分析,如国王游戏、耍杂技的牛等问题。
无论公式如何,贪心问题先排序

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值