简单介绍
算法定义
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。也就是说,不从整体最优上加以考虑,做出的只是在某种意义上的局部最优解。
详细介绍
算法思想
贪心算法一般按如下步骤进行:
(1)建立数学模型来描述问题。
(2)把求解的问题分成若干个子问题。
(3)对每个子问题求解,得到子问题的局部最优解。
(4)把子问题的解局部最优解合成原来解问题的一个解。
贪心算法是一种对某些求最优解问题的更简单、更迅速的设计技术。贪心算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择,就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解。虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪心算法不要回溯。
算法特性
贪心算法可解决的问题通常大部分都有如下的特性:
1、有一个以最优方式来解决的问题。为了构造问题的解决方案,有一个候选的对象的集合:比如不同面值的硬币。
2、随着算法的进行,将积累起其他两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。
3、有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。
4、还有一个函数检查是否一个候选对象的集合是可行的,即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。
5、选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。
6、最后,目标函数给出解的值。
算法使用
利用贪心法求解的问题应具备如下2个特征。
1、贪心选择性质
一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键所在。在实际应用中,至于什么问题具有什么样的贪心选择性质是不确定的,需要具体问题具体分析。
算法缺点
贪心算法也存在如下问题:
1、不能保证解是最佳的。因为贪心算法总是从局部出发,并没从整体考虑;
2、贪心算法一般用来解决求最大或最小解;
3、贪心算法只能确定某些问题的可行性范围 。
算法框架
从问题的某一初始解出发;
while (能朝给定总目标前进一步)
{
利用可行的决策,求出可行解的一个解元素;
}
由所有解元素组合成问题的一个可行解;
实例演示
题目描述
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组flowerbed
表示花坛,由若干0
和1
组成,其中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<l
或 k>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
时根据prev
和i
的值计算上一个区间内可以种植花的最多数量,然后令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");
}