常见算法思想——贪心法

简单介绍

算法定义

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

详细介绍

算法思想

贪心算法一般按如下步骤进行:
(1)建立数学模型来描述问题。
(2)把求解的问题分成若干个子问题。
(3)对每个子问题求解,得到子问题的局部最优解。
(4)把子问题的解局部最优解合成原来解问题的一个解。

    贪心算法是一种对某些求最优解问题的更简单、更迅速的设计技术。贪心算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择,就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解。虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪心算法不要回溯。

算法特性

贪心算法可解决的问题通常大部分都有如下的特性:
1、有一个以最优方式来解决的问题。为了构造问题的解决方案,有一个候选的对象的集合:比如不同面值的硬币。
2、随着算法的进行,将积累起其他两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。
3、有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。
4、还有一个函数检查是否一个候选对象的集合是可行的,即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。
5、选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。
6、最后,目标函数给出解的值。

算法使用

利用贪心法求解的问题应具备如下2个特征。
1、贪心选择性质
一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键所在。在实际应用中,至于什么问题具有什么样的贪心选择性质是不确定的,需要具体问题具体分析。

算法缺点

贪心算法也存在如下问题:
1、不能保证解是最佳的。因为贪心算法总是从局部出发,并没从整体考虑;
2、贪心算法一般用来解决求最大或最小解;
3、贪心算法只能确定某些问题的可行性范围 。

算法框架

从问题的某一初始解出发;
while (能朝给定总目标前进一步)
{
利用可行的决策,求出可行解的一个解元素;
}
由所有解元素组合成问题的一个可行解;

实例演示

题目描述

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 01 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false
示例 1:
输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
示例 2:
输入:flowerbed = [1,0,0,0,1], n = 2
输出:false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/can-place-flowers

题目解析

   判断能否在不打破种植规则的情况下在花坛内种入 n 朵花,从贪心的角度考虑,应该在不打破种植规则的情况下种入尽可能多的花,然后判断可以种入的花的最多数量是否大于或等于 n。

   假设花坛的下标 i 和下标 j 处都种植了花,其中 j-i ≥2,且在下标 [i+1,j−1] 范围内没有种植花,则只有当 j-i≥4 时才可以在下标 i 和下标 j 之间种植更多的花,且可以种植花的下标范围是 [i+2,j−2]。可以种植花的位置数是 p=j−i−3,当 p 是奇数时最多可以在该范围内种植(p+1)/2 朵花,当 p 是偶数时最多可以在该范围内种植p/2 朵花。由于当 p 是偶数时,在整数除法的规则下 p/2(p+1)/2 相等,因此无论 p 是奇数还是偶数,都是最多可以在该范围内种植 (p+1)/2 朵花,即最多可以在该范围内种植(j−i−2)/2 朵花。

   上述情况是在已有的两朵花之间种植花的情况(已有的两朵花之间没有别的花)。假设花坛的下标 l 处是最左边的已经种植的花,下标 r 处是最右边的已经种植的花(即对于任意 k<lk>r都有flowerbed[k]=0),如何计算在下标 l 左边最多可以种植多少朵花以及在下标 r 右边最多可以种植多少朵花?

    下标 l 左边有 l 个位置,当 l<2时无法在下标 l 左边种植花,当l≥2 时可以在下标范围[0,l−2] 范围内种植花,可以种植花的位置数是 l−1,最多可以种植l/2 朵花。

    m 为数组 flowerbed 的长度,下标 r 右边有 m−r−1 个位置,可以种植花的位置数是 m-r-2,最多可以种植(m−r−1)/2 朵花。

    如果花坛上没有任何花朵,则有 m 个位置可以种植花,最多可以种植 (m+1)/2 朵花。

    根据上述计算方法,计算花坛中可以种入的花的最多数量,判断是否大于或等于 n 即可。具体做法如下:

维护 prev 表示上一朵已经种植的花的下标位置,初始时prev=−1,表示尚未遇到任何已经种植的花。

从左往右遍历数组flowerbed,当遇到flowerbed[i]=1 时根据previ
的值计算上一个区间内可以种植花的最多数量,然后令 prev=i,继续遍历数组 flowerbed 剩下的元素。

遍历数组flowerbed 结束后,根据数组prev 和长度 m 的值计算最后一个区间内可以种植花的最多数量。

判断整个花坛内可以种入的花的最多数量是否大于或等于 n

来源:力扣(LeetCode)

题目算法

bool canPlaceFlowers(int* flowerbed, int flowerbedSize, int n) {
    int count = 0;
    int prev = -1;
    for (int i = 0; i < flowerbedSize; ++i) {
        if (flowerbed[i] == 1) {
            if (prev < 0) {
                count += i / 2;
            } else {
                count += (i - prev - 2) / 2;
            }
            prev = i;
        }
    }
    if (prev < 0) {
        count += (flowerbedSize + 1) / 2;
    } else {
        count += (flowerbedSize - prev - 1) / 2;
    }
    return count >= n;
}

完整代码

#include<stdio.h>
#define true 1
#define false 0

bool canPlaceFlowers(int* flowerbed, int flowerbedSize, int n) 
{
    int count = 0;
    int prev = -1;
    for (int i = 0; i < flowerbedSize; ++i) 
    {
        if (flowerbed[i] == 1)
        {
            if (prev < 0) 
            {
                count += i / 2;
            }
            else 
            {
                count += (i - prev - 2) / 2;
            }
            prev = i;
        }
    }
    if (prev < 0) 
    {
        count += (flowerbedSize + 1) / 2;
    }
    else 
    {
        count += (flowerbedSize - prev - 1) / 2;
    }
    return count >= n;
}

int main()
{
    int flowerbed[] = { 1,0,0,0,1 }, flowerbedSize = 5, n = 2;
    //int flowerbed[] = { 1,0,0,0,1 }, flowerbedSize = 5, n = 1;
    int index;
    index = canPlaceFlowers(flowerbed, flowerbedSize, n);
    //printf("%d", index);
    if (index == true)
        printf("true\n");
    else if(index == false)
        printf("false\n");
}
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值