ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

贪心算法详细介绍(贪心算法与动态规划的区别)

2022-01-16 23:33:20  阅读:151  来源: 互联网

标签:问题 硬币 选择 算法 最优 动态 贪心


贪心算法

当一个问题具有最优子结构性质时,可用动态规划法求解。有时会有更简单有效的算法。考察找硬币的例子。假设有4种硬币,它们的面值分别为二角五分、一角、五分和一分。现在要找给顾客六角三分钱。这时,自然地拿出2个二角五分的硬币、1个一角的硬币和3个一分的硬币交给顾客。这种找硬币方法与其他找法相比,拿出的硬币个数是最少的。这里使用的找硬币算法为:首先选出一个面值不超过六角三分的最大硬币,即二角五分;然后从六角三分中减去三角五分,剩下三角八分;再选出一个面值不超过三角八分的最大硬币,即又一个二角五分,如此一直做下去。这个方法实际上就是贪心算法。顾名思义,贪心算法总是做出在当前看来是最好的选择。 也就是说,贪心算法并不从整体最优加以考虑,所做的选择只是在某种意义上的最优选择。当然,我们希望贪心算法得到的最终结果也是整体最优的。找硬币算法得到的结果就是一个整体最优解。

找硬币问题本身具有最优子结构性质,可以用动态规划算法来解,但贪心算法更简单,更直接,并且解题效率更高。这利用了问题本身的一些特性。例如,上述找硬币的算法利用了硬币面值的特殊性。如果硬币面值改为一分、五分和一角一分,而要找给顾客的是一角五分钱。还用贪心算法,将给顾客1个一角一分的硬币和4个一分的硬币。然而3个五分的硬币显然是最好的找法。虽然贪心算法不是对所有问题都可以得到整体最优解,但是对范围相当广的许多问题能够产生整体最优解,如最小生成树问题、图的单源最短路径问题等。在一些情况下,即使贪心算法不能得到整体最优解,但其最终结果却是最优解的很好的近似解。

例:活动选择问题

题目描述:设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si <fi 。如果选择了活动i,则它在半开时间区间[si, fi)内占用资源。若区间[si, fi)与区间[sj, fj)不相交,则称活动i与活动j是相容的。也就是说,当si≥fj或sj≥fi时,活动i与活动j相容。

活动安排问题: 要在所给的活动集合中选出最大的相容活动子集合。

活动安排问题的关键是如何按照一定的顺序安排活动,使得选出的活动间相容并能安排尽量多的活动。

题解:由于输入的活动以其完成时间的非减序排列,所以每次总是选择具有最早完成时间的相容活动加入集合A中。直观上,按这种方法选择相容活动为未安排活动留下尽可能多的时间。也就是说,贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。

若被检查的活动i的开始时间Si小于最近选择的活动j的结束时间fi,则不选择活动i,否则选择活动i加入集合A中。

贪心算法并不总能求得问题的整体最优解。但对于活动安排问题,贪心算法greedySelector却总能求得的整体最优解,即它最终所确定的相容活动集合A的规模最大。这个结论可以用数学归纳法证明。

#include<iostream>
using namespace std;
/*假设输入的活动结束时间按照增序排序。
若输入活动的结束时间未排序,可对活动进行排序然后进行选择。
*/
 
int s[12],f[12];
bool a[11];
int n=11;
int Selector()
{
    a[1]=true;
    int j=1;
    int count=1;
    for (int i=2;i<=n;i++) 
	{
		if(s[i]>=f[j]) 
		{   
			a[i]=true;   
			j=i;     
			count++;          
		}
        else 
			a[i]=false;
	}
	return count;
}
 
int main()
{
	cout<<"活动序号:"<<endl;
	for(int i=1;i<=11;i++)
		cout<<i<<" ";
	cout<<endl<<"活动开始时间:"<<endl;
	for(int i=1;i<=11;i++)
		cout<<s[i]<<" ";
	cout<<endl<<"活动结束时间:"<<endl;
	for(int i=1;i<=11;i++)
		cout<<f[i]<<" ";
	int count=Selector();
	cout<<endl<<"一共选择个"<<count<<"活动如下:"<<endl;
	for(int i=0;i<=n;i++)
		if(a[i])
			cout<<i<<" ";
	return 0;
}

贪心算法的基本要素

贪心算法通过一系列选择来得到问题的解,所做的每个选择都是当前状态下局部最好选择,即贪心选择。许多可以用贪心算法求解的问题中可以看到,它们一般具有两个重要的性质:贪心选择性质和最优子结构性质。

1. 贪心选择性质
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。在动态规划算法中,每步所做的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才可以做出选择。而在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。 再去解做出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖以往所做过的选择,但决不依赖将来所做的选择,也不依赖子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。证明的大致过程为:首先考察问题的一个整体最优解,并证明可修改这个最优解,使其以贪心选择开始。做了贪心选择后,原问题简化为规模更小的类似子问题。然后用数学归纳法证明通过每一步做贪心选择,最终可得到问题的整体最优解。其中,证明贪心选择后的问题简化为规模更小的类似子问题的关键在于利用该问题的最优子结构性质。

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

3. 贪心算法和动态规划算法的差异

共同点: 贪心算法和动态规划算法都要求问题具有最优子结构性质,这是这两类算法的一个共同点。
区别: 动态规划算法中,每步所做的选择往往依赖于相关子问题的解,因而只有在解出相关子问题时才能做出选择。而贪心算法,仅在当前状态下做出最好选择,即局部最优选择,然后再去解做出这个选择后产生的相应的子问题。

贪心:每一步的最优解一定包含上一步的最优解,上一步之前的最优解则不作保留;

动态规划:全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有的局部最优解 。

举例说明:也就是说,假如你要求第十步的最优解,那么第十步的最优解肯定与第九步的最优解有关,而第九步的最优解肯定与第八步的最优解有关。可以这么理解,贪心算法第十步的最优解得把前面九步的最优解都用上了,但是动态规划你需要求第十步的最优解,这个最优解可能只与第八步,第三步,第一步有关,与第九步没有关系,我们为什么选择第八步而不选择第九步呢?是因为我们在计算第十步的最优解的时候其实把1-9步的组合的情况都计算了,选择了其中最优的解,也就是第八步的解,其实第十步解的构成与第九步没有关系,动态规划相当于穷举了1-9步最优情况下的组合,选了其中最优的作为第十步的最优解,而贪心算法第十步的最优解肯定是由第九步构成的。

求一个问题的最优解相当于遍历所有的子集来找最优解,但是这样解随着解空间的维度成指数增长,动态规划其实就是一种遍历,但是他是带备忘录的遍历,对于前面算到的子问题,仅就不在计算而是直接查看备忘录,直接调用之前保存的值,这样就节省了大量的时间。

标签:问题,硬币,选择,算法,最优,动态,贪心
来源: https://blog.csdn.net/wangzhening/article/details/122529620

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有