贪心——一种算法思想

贪心算法

贪心算法(又称贪婪算法)是指,在对 问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部 最优解。

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
在这里插入图片描述

贪心选择

贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。贪心选择是采用从顶向下、以迭代的方法做出相继选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择的性质,我们必须证明每一步所作的贪心选择最终能得到问题的最优解。通常可以首先证明问题的一个整体最优解,是从贪心选择开始的,而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步贪心选择,最终可得到问题的一个整体最优解。

最优子结构

在这里插入图片描述
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。运用贪心策略在每一次转化时都取得了最优解。问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。动态规划主要运用于二维或三维问题,而贪心一般是一维问题。

那贪心算法能解决哪些事呢?

随着算法的进行,将积累起其它两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。

有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。

还有一个函数检查是否一个候选对象的集合是可行的,也即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。

选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。

最后,目标函数给出解的值。

为了解决问题,需要寻找一个构成解的候选对象集合,它可以优化目标函数,贪心算法一步一步的进行。起初,算法选出的候选对象的集合为空。接下来的每一步中,根据选择函数,算法从剩余候选对象中选出最有希望构成解的对象。如果集合中加上该对象后不可行,那么该对象就被丢弃并不再考虑;否则就加到集合里。每一次都扩充集合,并检查该集合是否构成解。如果贪心算法正确工作,那么找到的第一个解通常是最优的。

贪心算法不是从整体最优的角度考虑,而是仅仅获得某种意义上的局部最优解。虽然贪心算法不是对所有问题都能得到整体最优解,但是对范围相当广泛的许多问题都能产生整体最优解或者其近似解。

下面通过例题来体现贪心的思想

A公司的计算机管理系统受到千年虫病毒的攻击,因此A公司丢失了向MS公司做年终回报的数据。 A公司目前掌握的数据是MS公司每次公布的公司亏盈报表,而MS公司公布亏盈的方式与众不同,它么次都是将连续5个月的亏盈总和做一次性公布,因此A公司不知道每个月具体的亏盈状况。已知的情况是所有的盈利月的盈利固定为s,而亏损月的亏损固定为 d 。

写一个程序,确定MS公司是否盈利,若盈利的话,计算可能的盈利最大值。

输入:

输入为两个整数 s 和 d 。

输出:

对于每一组的输入数据,若盈利的话,那么输出可能盈利最大值;若亏损的话,输出 Deficit。

样例输入:

59 237

375 743

200000 849694

2500000 8000000

样例输出:

116

28

300612

Deficit

#include<iostream>
using namespace std;
int main()
{
     int s,d;
     while(scanf("%d%d",&s,&d)!=EOF)
     {
         int i,ans;
         for(i=1;i<=5;i++)
              if(s*(5-i)-d*i<0)
                   break;             //枚举出五个月中至少有i个亏损月
         if(i==4)
                  ans=3*s-9*d;
         else
                 ans=s*(12-2*i)-d*2*i;            //i==5时此公式不适用,但可直接判断全年亏损
         if(i==5||ans<0)                             //i==5这个判断条件必须放在ans<0前
                 printf("Deficit\n");
         else
                printf("%d\n",ans);
    }
}

1.解题思路:首先,每连续5个月至少有哪几个月是亏损的是可以算出来的。
用贪心的思想可以解决如何安排亏损月份的问题:假设5个月至少亏损3个月,那么对前5个月来说,亏损的月份必定是3,4,5月,这样才能保证这3个亏损月能最大可能地被后面连续的5个月利用,以减少出现更多的亏损月。所以首先计算出连续的5个月至少有哪几个月是亏损的,再根据贪心计算全年的盈亏。

——许多可以用贪心算法求解的问题也可以表示为求带权拟阵的最大权独立子集问题呀
给定带权拟阵M=(S;I),确定S的独立子集A∈I,使得W(A)达到最大。这种使W(A)最大的独立子集A称为拟阵M的最优子集。由于S中任一元素x的权W(x)是正的,所以最优子集也一定是极大独立子集。
例如最小生成树问题可以表示为确定带权拟阵的最优子集问题。求带权拟阵的最优子集A的算法可用于解最小生成树问题。

下面给出求带权拟阵最优子集的贪心算法。该算法把具有正权函数W的带权拟阵M=(S;I)作为输入,计算后输出M的最优子集A。

Set greedy(M,W)
{
      A= ∅ ;
      将S中的元素依权值W(大者优先)组成优先队列
      while(S!= ∅ )
      {
               S.removeMax(x);
               if(A∪{x}∈I)
                  A=A∪{x};  
      }
      return A;
}

引理1.1
拟阵的贪心选择性质:
设M=(S,I)是具有权函数W的带权拟阵,且S中元素依权值从大到小排列。又设x ∈S是S中第一个使得{x}是独立子集的元素,则存在S的最优子集A使得x ∈S。
greedy算法在以贪心法选择构造最优子集A时,首次选入集合A中的元素x是单元素独立集中具有最大权的元素。此时可能已经舍弃了S中部分元素。可以证明这些被舍弃的元素不可能用于构造最优子集。

引理1.2
设M=(S;I)是拟阵。若S中的元素x不是空集 ∅的可扩展元素,则x也不可能是S中任一独立子集A的可扩展元素。

引理1.3
拟阵的最优子结构性质:
设x是求带权拟阵M=(S,I)的最优子集的贪心算法greedy所选择的S中的第一个元素,那么,原问题可简化为求带权拟阵M′=(S’,I’)的最优子集问题,其中:
S’={y|y∈S,{x,y}∈I}
I’={B|B⊆S-{x},B∪{x}∈I}
M’的权函数是M的权函数在S’上的限制(称M’为M关于元素x的收缩)。
定理
带权拟阵贪心算法的正确性。
设M=(S,I)是具有权函数W的带权拟阵,greedy算法返回M的最优子集。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值