• 收藏 | C语言最常用的贪心算法


    01

    基本概念

    贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

    贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性(即某个状态以后的过程不会影响以前的状态,只与当前状态有关。)

    所以,对所采用的贪心策略一定要仔细分析其是否满足无后效性。

    02

    贪心算法的基本思路

    解题的一般步骤是:

    1.建立数学模型来描述问题;
    2.把求解的问题分成若干个子问题;
    3.对每一子问题求解,得到子问题的局部最优解;
    4.把子问题的局部最优解合成原来问题的一个解。

    03

    该算法存在的问题

    • 不能保证求得的最后解是最佳的

    • 不能用来求最大值或最小值的问题

    • 只能求满足某些约束条件的可行解的范围

    04

    贪心算法适用的问题

    贪心策略适用的前提是:局部最优策略能导致产生全局最优解。

    实际上,贪心算法适用的情况很少。一般对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可以做出判断。

    05

    贪心选择性质

    所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,换句话说,当考虑做何种选择的时候,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。这是贪心算法可行的第一个基本要素。贪心算法以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

    当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心算法求解的关键特征。

    06

    贪心算法的实现框架

    从问题的某一初始解出发:

    1. while (朝给定总目标前进一步)
    2. {
    3. 利用可行的决策,求出可行解的一个解元素。
    4. }

    由所有解元素组合成问题的一个可行解;

    07

    例题分析

    如果大家比较了解动态规划,就会发现它们之间的相似之处。最优解问题大部分都可以拆分成一个个的子问题,把解空间的遍历视作对子问题树的遍历,则以某种形式对树整个的遍历一遍就可以求出最优解,大部分情况下这是不可行的。贪心算法和动态规划本质上是对子问题树的一种修剪,两种算法要求问题都具有的一个性质就是子问题最优性(组成最优解的每一个子问题的解,对于这个子问题本身肯定也是最优的)。动态规划方法代表了这一类问题的一般解法,我们自底向上构造子问题的解,对每一个子树的根,求出下面每一个叶子的值,并且以其中的最优值作为自身的值,其它的值舍弃。而贪心算法是动态规划方法的一个特例,可以证明每一个子树的根的值不取决于下面叶子的值,而只取决于当前问题的状况。换句话说,不需要知道一个节点所有子树的情况,就可以求出这个节点的值。由于贪心算法的这个特性,它对解空间树的遍历不需要自底向上,而只需要自根开始,选择最优的路,一直走到底就可以了。

    话不多说,我们来看几个具体的例子慢慢理解它:

    1.活动选择问题

    这是《算法导论》上的例子,也是一个非常经典的问题。有n个需要在同一天使用同一个教室的活动a1,a2,…,an,教室同一时刻只能由一个活动使用。每个活动ai都有一个开始时间si和结束时间fi 。一旦被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这一天。该问题就是要安排这些活动使得尽量多的活动能不冲突的举行。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。


    考虑使用贪心算法的解法。为了方便,我们用不同颜色的线条代表每个活动,线条的长度就是活动所占据的时间段,蓝色的线条表示我们已经选择的活动;红色的线条表示我们没有选择的活动。

    如果我们每次都选择开始时间最早的活动,不能得到最优解:

    如果我们每次都选择持续时间最短的活动,不能得到最优解:

    可以用数学归纳法证明,我们的贪心策略应该是每次选取结束时间最早的活动。直观上也很好理解,按这种方法选择相容活动为未安排活动留下尽可能多的时间。这也是把各项活动按照结束时间单调递增排序的原因。
    C语言知识汇总

    C语言知识汇总

    1. #include<cstdio>
    2. #include<iostream>
    3. #include<algorithm>
    4. using namespace std;
    5. int N;
    6. struct Act
    7. {
    8. int start;
    9. int end;
    10. }act[100010];
    11. bool cmp(Act a,Act b)
    12. {
    13. return a.end<b.end;
    14. }
    15. int greedy_activity_selector()
    16. {
    17. int num=1,i=1;
    18. for(int j=2;j<=N;j++)
    19. {
    20. if(act[j].start>=act[i].end)
    21. {
    22. i=j;
    23. num++;
    24. }
    25. }
    26. return num;
    27. }
    28. int main()
    29. {
    30. int t;
    31. scanf("%d",&t);
    32. while(t--)
    33. {
    34. scanf("%d",&N);
    35. for(int i=1;i<=N;i++)
    36. {
    37. scanf("%lld %lld",&act[i].start,&act[i].end);
    38. }
    39. act[0].start=-1;
    40. act[0].end=-1;
    41. sort(act+1,act+N+1,cmp);
    42. int res=greedy_activity_selector();
    43. cout<<res<<endl;
    44. }
    45. }

    2.钱币找零问题

    这个问题在我们的日常生

  • 相关阅读:
    Oracle绑定SQL执行计划
    外部 prometheus监控k8s集群资源
    Jmeter如何做压力测试
    2024年网络安全/黑客自学路线图
    jmeter JDBC Request参数化
    SQL server 一次把一列的所有值都取出来保存到一个变量里
    Vulnhub系列靶机---Raven: 2
    docker-compose一键部署mysql
    辐射骚扰整改思路及方法:对共模电流的影响?|深圳比创达电子EMC
    第5章 课后习题实训---二维数组
  • 原文地址:https://blog.csdn.net/yx5666/article/details/127670066