跳转至

贪心

前置知识

排序

目标

贪心典型例题

What

贪心算法的核心就是一个字:贪。总是做出当前看来最优的选择。

因此可知,贪心算法不从整体去考虑,它做出的选择也是局部最优选择,从而达到全局优化选择。

我们只关注目前的利益,从而达到利益最大化,尽管贪心算法不一定能得到最优解,面对相当数量的一部分问题,我们是可以得到正确的答案的。

从问题的某一个初始解,一步步推论,保证每一步都是最为优化的,出发逐步逼近给定的目标,以尽可能快的地求得更好的解。

当接下来的任意一步都无法达到最优时,贪心算法结束。

贪心常见题型

1、按某种顺序排序后,然后逐个取(sort,多关键字排序,重载小于符号)

2、每次取集合中的最大/最小,更新答案(使用priority_queue)

贪心在最优子结构的问题中尤为有效(最优子结构的意思是问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解)。

贪心与dp的区别,贪心对每个子问题的解决方案都做出选择,不能回退。dp会保存以前的运算结果,并根据以前的结果进行选择,有回退功能。

有的时候,贪心并不是正确的,比如 01背包问题,贪心就会出错。贪心问题特别像逻辑题,方法很简单,但是证明却很难。考场上,不需要会证明。

贪心法证明的常见方法

  • 反证法(假设、调整、做差)
  • A>=B, A <= B, 证明A=B
  • 数学归纳法

例题,【例6.1】排队接水

// 题意:给出n个人每个人打水需要的时间,现在有一个水龙头,问这n个人完成打水,平均等待时间最小

#include <bits/stdc++.h>

using namespace std;

const int N = 5010;

struct node{
    int id;
    int time;
}a[N];

bool cmp1(node a, node b)
{
    return a.time < b.time;
}

int main()
{
    int n;
    cin >> n;

    for (int i = 1; i <= n; i++){
        cin >> a[i].time;
        a[i].id = i;
    }

    sort(a + 1, a + 1 + n, cmp1);

    double sum = 0;
    for (int i = 1; i <= n; i++){
        if (n - i >= 1) sum += a[i].time * (n - i);
    }

    for (int i = 1; i <= n; i++) cout << a[i].id << ' ';
    puts("");
    printf("%.2lf\n", sum / n);

    return 0;
}

题单

一刷:

【例6.5】活动选择

【例6.6】整数区间

【例6.4】拦截导弹问题(Noip1999)

【例6.3】删数问题(Noip1994)

1233:接水问题

二刷:

Tian Ji -- The Horse Racing

1425:【例题4】加工生产调度

1430:家庭作业

POJ3614 Sunscreen

POJ3190 Stall Reservations

总结

贪心比较需要思维能力,最开始刚接触,没有什么解题经验的同学,我觉得可以不必从贪心入手。

先把其他算法都学了,贪心算法就知道一个理论,然后加上典型类型的例题,就可以去刷题了。

至于证明贪心,普及组不多加要求。

参考

【普及】贪心算法专项训练 - 题单 - 洛谷

维基百科-贪心