算法导论13.贪心算法

摘要:
与动态规划类似,贪婪算法也将问题简化为更小的子问题,并通过递归求解子问题来获得整个问题的解。0-1背包问题是NP问题,所以贪婪算法不能保证最优解。如果小偷遵循贪婪算法,他就无法获得最优解。该算法可以被证明是最优的,也就是说,假设加油站序列是$S={S_1,S_2…S_n}$,并且贪婪解中的第一个加油站是$S_I$,则必须处于最优解中。

与动态规划类似,贪心算法也将问题化简为规模较小的子问题,并通过递归解决子问题来获取整个问题的解。不同的是,贪心问题不对子问题进行比较,而是只生成一个非空的子问题,而使选择在当时看上去是最优的(即“贪心”的含义)

活动选择问题

几个互相竞争的活动都要求以独占的方式占用某个公用资源(如选修课程对个人可支配时间的要求,或会议对会议室的要求),每个活动都有开始时间和结束时间。如何安排活动,使在总时间内开展的活动次数最多?形式化的,一组活动组成的集合 $S={a_1,a_2...a_n}$ ,每一个活动 $a_i$ 都有开始时间 $s_i$ 和结束时间 $f_i$ 。求一组互相兼容的活动的最大子集合 $S'$。

贪心算法:

将所有活动按照结束时间排序,得到新的 $S$,首先选取结束时间最小 $a_1$ 的加入解 $S'$,然后检查 $a_2$ ,如果 $a_2$ 的开始时间大于当前已加入解的所有活动(目前就一个 $a_1$)的结束时间,那么就舍弃之,否则将其加入解 $S'$ ,再检查 $a_3$,以此类推。

能够证明,贪心算法获取的是全局最优解:

我们可以说,在排序后的 $S$,第一个活动 $a_1$(结束时间最早的活动)一定在最优解 $S'$ 中。为什么呢?因为假设 $S'$ 不包含 $a_1$,那么一定包含一个与 $a_1$ 不兼容的 $a_i$ ,是吧?(如果这都不能满足,直接向 $S'$ 中加入 $a_1$ ,得到的新子集就比 $S'$ 多了一个元素,那么 $S'$ 就不是最优解了)然后,我们可以断言,从 $S'$ 去掉 $a_i$ 再加上 $a_1$,$S'$ 仍然是最优解。

为什么呢?因为 $a_1$ 的结束时间是最早的,所以 $a_i$ 与 $a_1$ 不兼容的方式只能是:$a_i$ 的开始时间比 $a_1$ 结束时间早(但结束时间比 $a_1$ 结束时间晚),而且因为 $S'$ 中除了 $a_i$ 的其他活动都与 $a_i$ 兼容(理所当然,否则 $S'$ 连解都不是,何谈最优解?),这些其他活动都会出现在 $a_i$ 后面(也就是这些活动的开始时间比 $a_i$ 的结束时间晚),而不是前面。如果出现在前面呢? 拜托,如果这些活动在 $a_i$ 前面,那么这些活动的结束时间比 $a_i$ 的开始时间早,也就是比 $a_1$ 的结束时间早。这怎么可能呢?$a_1$ 就是$S$ 中具有最早结束时间的活动了啊。

原理说起来拗口,实现做起来却相当简单:

class actvty{
public:
    actvty(double start, double end, int id):_start(start),_end(end),_id(id){}
    const double getStart() const {return _start;}
    const double getEnd() const {return _end;}
    const int getId() const {return _id;}
    bool operator<(const actvty& tmp) const {return getEnd()<tmp.getEnd();}
private:
    double _start;
    double _end;
    int _id;
};

std::vector<actvty> slctn(std::vector<actvty>& all){
    std::sort(all.begin(), all.end());
    std::vector<actvty> rslt;
    double start = 0;
    for (int i=0; i<all.size(); i++){
        if (all[i].getStart() >= start){
            rslt.push_back(all[i]);
            start = all[i].getEnd();
        }
    }
    return rslt;
}

练习16.1-3 假设用多个资源对一组活动进行调度,我们希望使用尽可能少的资源来满足所有活动要求。

思路:和活动选择问题类似,只不过当一个活动不能被(当前开辟的资源)满足时,我们不是将它舍弃,而是新开辟一个资源供其使用。

0-1背包问题

窃贼在偷窃一家商店时发现有 $n$ 件物品 $S=a_1,a_2...a_n$ ,每一件物品 $a_i$ 都有其价值 $v_i$ 和重量 $w_i$ ,作为一个有尊严的窃贼,他曾发誓每次只偷最多 $W$ 重量的货物,那么如何在不违背誓言的情况下,从赃物中获得最大的价值?

贪心算法:

将所有物品按照 $v_i/w_i$ 值(姑且称为“单价”吧)排序,然后先拿单价最高的,然后拿单价次高的,如果拿到一个物品后超重了,那就舍弃它并继续上述过程,直到商店里所有剩下物品中的任意一件都会使盗贼的赃物超重。

0-1背包问题是NP的,所以贪心算法不保证最优解。一个简单的例子就是:窃贼只偷取 10kg 物体,商店里只有两件物品,一件1kg,价值20元,单价20,另一件9.5kg,价值950元,单价10元。如果窃贼遵循贪心算法,就不能获得最优解了。

习题16.2-4 某教授驾车从 A 地到 B 地,总里程数确定。油箱中的汽油能支持的里程也是确定的,途中有若干个加油站。教授希望加油的次数尽量少,请确定需要加油的加油站,支持教授的车驶完全程。(假设教授出发时邮箱是满的)。

贪心算法:当途径某个加油站时,只有当油箱中的油已经不足以撑到下一个加油站时,才去加满油。

这个算法可以证明是最优的,即假设加油站序列为 $S={s_1,s_2...s_n}$,贪心解中的第一个加油站为 $s_i$ 一定在最优解中。如果 $s_i$ 不在最优解中,那么最优解中一定有一个加油站 $s_p$ 在 $s_i$ 之前,否则车开不到 $s_i+1$ 。假设最优解中的第二个加油站为 $s_q$ ,那么从最优解中去掉 $s_p$ 加上 $s_q$ 仍然是最优解。

习题16.2-7 两个集合 $A$ 和 $B$ ,各有 $n$ 个正整数,你可以按照自己的意愿对其进行排序。重新排序后,设 $a_i$ 为 $A$ 中的第 $i$ 个元素,$b_i$ 为 $B$ 中的第 $i$ 个元素。请排序元素,使 $\prod a_{i}^{b_{i}}$ 最大。

思路:全部递增排序就是。证明也很简单,就是证明当 $a_1>b_1$ 且 $a_2>b_2$ 时(当然都是正整数啊),$a_1^{b_1}>a_2^{b_2}$ 。

免责声明:文章转载自《算法导论13.贪心算法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇给11gR2 RAC添加LISTENER监听器并静态注册Idea快捷键大全下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

算法导论:Trie字典树

1、 概述  Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。  Trie一词来自retrieve,发音为/tri:/ “tree”,也有人读为/traɪ/ “try”。  Trie树可以利用字符串的公共前缀来节约存储空间。如下图所示,该trie树用10个节点保存了...

麻省理工公开课《算法导论》学习笔记:第一讲

主题:简介课程,渐近概念的大局观,插入排序和归并排序,递归式函数时间分析(递归树方法) 教材:《算法导论》 收获:很感动地看到算法分析那个log(n)是为什么出现了,更深层还要听第二讲,若不是因为要准备SAS,恨不得马上看。 内容: 1 何为算法分析? 计算机程序运行性能和存储空间的理论分析,叫算法分析。也就是关注2点:1 性能,就是程序跑得快不快; 2...

《算法导论》

序GitHub 见solution.txt 6.1 堆6.1-1 在高度为h的堆中,最多元素为2(h+1)−1个,最少元素有 2h+1 个 6.1-3 最大堆的特性是除了根结点外的每个结点都有A[PARENT(i)]>=A[i]故,在一个最大堆的某颗子树中最大元素必然位于该子树的根上。 6.1-4 根据最大堆的性质,任何子树的子结点都小于根节点,故...

算法导论10.顺序统计树与区间树习题

顺序统计树和区间树都是对红黑树的扩张:通过在节点添加字段完成其他的功能,如果该字段可以在 $O(1)$ 时间内维护,就能够不影响红黑树本身操作效率渐进量级。 顺序统计树 顺序统计树是红黑树的扩展:在红黑树的每个节点额外维护一个域size,记录以该节点为根的子树中的总结点个数。顺序统计数具有这样的功能:在 $O(\lg n)$ 时间内找到树中所有元素的第 $...

算法导论(一):渐进记号

算法导论(一):渐进记号 算法中,常用极限情况度量一个函数的好坏,因此引入了渐进函数的概念。在渐进函数中,有以下五种渐进记号:Ο、Θ、Ω、ο、ω。   假设有函数f(n)与函数g(n),如果令函数f(n)为实数a,函数g(n)为实数b,则有以下类比:   f(n)=Ο(g(n))  类似于  a≤b   f(n)=Ω(g(n))  类似于  a≥b   f...

算法导论 第十三章 红黑树(python)-1插入

红黑树是上一章二叉搜索树的改进,实现一种平衡 ,保证不会出现二叉树变链表的情况,基本动态集合操作的时间复杂度为O(lgn) 实际用途:c++stl中的set,map是用他实现的 红黑树的性质: 1.每个结点或是红色的,或是黑色的 2.跟结点是黑色的 3.每个叶结点(NIL)是黑色 4.如果一个结点是红色的,则它的两个结点都是黑色的 5.对每个结点,从该结点...