趣学算法系列-贪心算法

趣学算法系列-贪心算法

声明:本系列为趣学算法一书学习总结内容,在此推荐大家看这本算法书籍作为算法入门,
原作者博客链接,本书暂无免费电子版资源,请大家支持正版,更多的案例分析请查看原书内容。

第二章 贪心算法

一个贪心算法总是做出当前最好的选择,也就是说,它期望通过局部最优选择
从而得到全局最优的解决方案。
—《算法导论》
- 贪心算法求解两个重要的特性
(1)贪心选择 每一步都作出当前的最佳(何为最佳,策略不同,情况不同)选择 仅依赖于之前作出的选择
(2)最优子结构 问题的最优解包含其子问题的最优解
- 贪心算法秘籍
(1)贪心策略 选择当前看上去最好的一个方案 如最大的 最值钱的 最重的
(2)局部最优解 根据贪心策略,一步一步地得到局部最优解
(3)全局最优解 所有的局部最优解合成为原来问题的一个最优解
- 如何定义最优子结构
        分解问题模型 缩小问题的规模
        最优解子问题的分解结构和堆叠方式
        分解结构:每一步都在前一步的基础上选择当前最好的解(钱币找零问题)
        堆叠结构:分解成相对独立的几个子问题,最后进行合并(某种公式计算法则)
- 贪心算法的缺陷
         贪婪法每一步选择完之后,局部最优解就确定
        不再进行回溯,之前选择不再修改,直到算法结束
        贪心只有极少情况可以得到最优解
         通常得到的是近似最优解,但是简单高效
        省去了为找最优解可能需要的穷举操作
- 贪心算法的典型应用
        最优装载问题
        背包问题


  • 实际案例分析-最优装载问题

  • 问题描述

    有一天,海盗们截获了一艘装满各种各样古董的货船,每一件古董都价值连城,一旦打碎就失去了它
    的价值。虽然海盗船足够大,但载重量为 C,每件古董的重量为 wi,海盗们该如何把尽可能多数量的宝贝
    装上海盗船呢?

  • 问题分析

    要求装载的物品的数量尽可能多, 而船的容量是固定的, 那么优先把重量小的物品放进去, 在容量固定的情况下,
    装的物品最多。采用重量最轻者先装的贪心选择策略,从局部最优达到全局最优,从而产生最优装载问题的最优解。

  • 算法设计

    (1)当载重量为定值 c 时, wi 越小时,可装载的古董数量 n 越大。只要依次选择最小重量古董,直到不能再装为止。
    (2)把 n 个古董的重量从小到大(非递减)排序,然后根据贪心策略尽可能多地选出前 i 个古董,直到不能继续装为止,此时达到最优。

  • 完美图解

    每个古董的重量如表 2-1所示,海盗船的载重量 c 为 30,那么在不能打碎古董又不超过载重的情况下,怎么装入最多的古董?
    这里写图片描述

    (1)因为贪心策略是每次选择重量最小的古董装入海盗船,因此可以按照古董重量非递减排序,排序后如表 2-2 所示。
    这里写图片描述

    ( 2)按照贪心策略,每次选择重量最小的古董放入( tmp 代表古董的重量, ans 代表已装裁的古董个数)。
    i=0,选择排序后的第 1 个,装入重量 tmp=2,不超过载重量 30, ans =1。
    i=1,选择排序后的第 2 个,装入重量 tmp=2+3=5,不超过载重量 30, ans =2。
    i=2,选择排序后的第 3 个,装入重量 tmp=5+4=9,不超过载重量 30, ans =3。
    i=3,选择排序后的第 4 个,装入重量 tmp=9+5=14,不超过载重量 30, ans =4。
    i=4,选择排序后的第 5 个,装入重量 tmp=14+7=21,不超过载重量 30, ans =5。
    i=5,选择排序后的第 6 个,装入重量 tmp=21+10=31,超过载重量 30,算法结束。
    即放入古董的个数为 ans=5 个。

  • 伪代码详解

    (1)数据结构定义
             double w[N]; //一维数组存储古董的重量
    (2)按重量排序
             sort(w, w+n); //按古董重量升序排序
    (3)按照贪心策略找最优解
            首先用变量 ans 记录已经装载的古董个数, tmp 代表装载到船上的古董的重量,两个变
        量都初始化为 0。然后按照重量从小到大排序,依次检查每个古董, tmp 加上该古董的重量,
        如果小于等于载重量 c,则令 ans ++;否则,退出。

  • 实战演练

    //program 2-1
    
    #include 
    
    
    #include 
    
    const int N = 1000005;
    using namespace std;
    double w[N]; //古董的重量数组
    int main()
    {
    double c;
    int n;
    cout<<"请输入载重量 c 及古董个数 n: "<cin>>c>>n;
    cout<<"请输入每个古董的重量,用空格分开: "<for(int i=0;icin>>w[i]; //输入每个物品重量
    }
    sort(w,w+n); //按古董重量升序排序
    double temp=0.0;
    int ans=0; // tmp 为已装载到船上的古董重量, ans 为已装载的古董个数
    for(int i=0;iif(tmp<=c)
    ans ++;
    else
    break;
    }
    cout<<"能装入的古董最大数量为 Ans=";
    cout<return 0;
    }
  • 算法时间复杂度分析

    (1)时间复杂度:首先需要按古董重量排序,调用 sort 函数,其平均时间复杂度为 O(nlogn),
    输入和贪心策略求解的两个 for 语句时间复杂度均为 O(n),因此时间复杂度为 O(n + nlog(n))。
    (2)空间复杂度:程序中变量 tmp、 ans 等占用了一些辅助空间,这些辅助空间都是常
    数阶的,因此空间复杂度为 O(1)。

  • 你可能感兴趣的:(算法分析)