贪心算法简介:
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
基本思路
思想
贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止 。
过程
- 建立数学模型来描述问题;
- 把求解的问题分成若干个子问题;
- 对每一子问题求解,得到子问题的局部最优解;
- 把子问题的解局部最优解合成原来解问题的一个解。
算法特性编辑
贪婪算法可解决的问题通常大部分都有如下的特性:
随着算法的进行,将积累起其它两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。
有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。
还有一个函数检查是否一个候选对象的集合是可行的,也即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。
选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。
最后,目标函数给出解的值。
为了解决问题,需要寻找一个构成解的候选对象集合,它可以优化目标函数,贪婪算法一步一步的进行。起初,算法选出的候选对象的集合为空。接下来的每一步中,根据选择函数,算法从剩余候选对象中选出最有希望构成解的对象。如果集合中加上该对象后不可行,那么该对象就被丢弃并不再考虑;否则就加到集合里。每一次都扩充集合,并检查该集合是否构成解。如果贪婪算法正确工作,那么找到的第一个解通常是最优的
举个栗子:
还是上篇文中的 活动时间问题:
想要使用贪心算法的话,得先找到适合贪心算法的规律(局部最优选择)
对于任何非空的活动集合S,假如am是S中结束时间最早的活动,则am一定在S的某个最大兼容活动子集中。
代码实现:
using System;
namespace _5_1_1贪心算法_活动选择问题
{
class Program
{
static void Main(string[] args)
{
List<int> resultList = Activity(1, 11, 0, 24);
foreach (int item in resultList)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
//活动开始时间s,结束时间f
static int[] s = { 0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12, 24 };
static int[] f = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 24 };
//参数:开始/结束的活动个数,开始/结束活动的时间
public static List<int> Activity(int startNumber, int endNumber,int startTime,int endTime )
{
if (startNumber > endNumber || startTime >= endTime) return new List<int>();
//找到活动结束时间最早的活动
int temp = 0;
for (int i = startNumber; i <= endNumber; i++)
{
if (s[i] >= startTime && f[i] <= endTime)
{
temp = i;
break;
}
}
//找剩下的时间中结束时间最早的活动
List<int> list = Activity(temp + 1, endNumber, f[temp], endTime);
list.Add(temp);
return list;
}
}
}
迭代法更符合上面贪心算法的文字描述,,,
代码如下:
using System;
namespace _5_1_2贪心算法_活动选择问题_迭代
{
class Program
{
static void Main(string[] args)
{
// 活动开始时间s,结束时间f
int[] s = { 0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12, 24 };
int[] f = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 24 };
//开始结束活动的时间
int startTime = 0;
int ensTime = 24;
//存储最大活动个数的列表
List<int> list = new List<int>();
//从第一个活动到最后一个活动
for (int i = 1; i <= 11; i++)
{
//判断每个活动是否子活动时间区间内
if(s[i]>= startTime && f[i]<= ensTime)
{
//在则记录,不在不处理
list.Add(i);
//若在则只需要从此活动的结束时间继续搜索即可(更新活动时间)
startTime = f[i];
}
}
foreach (int item in list)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}