快速剖析贪心算法(C语言)

在这里插入图片描述14天阅读挑战赛

努力是为了不平庸~

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。

学习日记

目录

学习日记

一、贪心算法

二、算法思路

三、算法特性

四、用贪心法求解的问题应具备的特征 

五、贪心策略解

六、贪心算法存在的问题 

七、经典应用

八、小例 

钱币找零问题

 加油站问题


一、贪心算法

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

         贪心算法是一种在解决问题的过程中追求局部最优的算法,对于一个有多种属性的事物来说,贪心算法会优先满足某种条件,追求局部最优的同时希望达到整体最优的效果。以背包问题为例,可以放在背包中的物体有它的重量和价值两种属性,背包的容量也是有限的,我们希望得到一种价值最大的物品摆放方式,如果我们倾向于重量贪心,那么在摆放物品的时候会优先放重量小的,但这和我们追求的价值最优没有关系,自然不能采用;如果倾向于价值贪心,而忽略了物品的重量,可能会导致摆放物品的数量不多,总价值很小;如果是以价值和重量的比值设计贪心算法求解,便可以实现最优的方案。

         贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择

二、算法思路

贪心算法一般按如下步骤进行:

①建立数学模型来描述问题。

②把求解的问题分成若干个子问题 。

③对每个子问题求解,得到子问题的局部最优解。

④把子问题的解局部最优解合成原来解问题的一个解。

        贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择,就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解。虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪心算法不要回溯。

三、算法特性

        有一个以最优方式来解决的问题。为了构造问题的解决方案,有一个候选的对象的集合:比如不同面值的硬币。

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

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

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

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

四、用贪心法求解的问题应具备的特征 

1、贪心选择性质

        一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

2、最优子结构性质

        当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键所在。在实际应用中,至于什么问题具有什么样的贪心选择性质是不确定的,需要具体问题具体分析。

五、贪心策略解

        贪心策略要注意局部最优与全局最优的关系,选择当前的局部最优并不一定能推导出问题的全局最优。        

1、该问题是否适合使用贪心策略求解,也就是该问题是否具有贪心选择性质。

2、制定贪心策略,以达到问题的最优解或较优解。

六、贪心算法存在的问题 

1、不能保证解是最佳的。因为贪心算法总是从局部出发,并没从整体考虑。

2、贪心算法一般用来解决求最大或最小解。

3、贪心算法只能确定某些问题的可行性范围。

七、经典应用

 霍夫曼编码,普利姆和克鲁斯卡尔最小生成树算法,还有迪杰斯特拉单源最短路径算法等,以及一些常见的找零问题。

八、小例 

钱币找零问题

        假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?

#include<stdio.h>
#include<stdlib.h>

int main() {
	int m[7]= {1,2,5,10,20,50,100}; //先有纸币的数额
	int k;
	//所找的金额
	int num;          //所需纸币的张数
	while((	scanf("%d",&k))!=EOF) {
		while(k>0) {
			if(k>=100) {
				k-=100;
			} else if(k>=50&&k<100) {
				k-=50;
			} else if(k>=20&&k<50) {
				k-=20;
			} else if(k>=5&&k<20) {
				k-=5;
			} else if(k>=2&&k<5) {
				k-=2;
			} else if(k>=1&&k<2) {
				k-=1;
			}
			num++;
		}
		printf("所需的张数为:%d\n",num);
	}
}

 加油站问题

        一辆汽车加满油后可以行驶n千米,旅途中有若干个加油站(加油站是已经确定好的),为了使沿途加油次数最少,设计一个算法,输出最好的加油方案。
        例如:假设沿途有9个加油站,总路程为100千米,加满油后汽车行驶的最远距离为20千米。

#include <stdio.h>
#define S 100  //S是全程长度
void main()
{
	int i, j, n, k=0, total, dist;
	int x[]= {10, 20, 35, 40, 50, 65, 75, 85, 100};  //加油站距离起点的位置
	int a[10];  //选择加油点的位置 
	n= sizeof(x)/sizeof(x[0]);   //沿途加油站的个数
	printf("请输入最远行车距离(15<=n<100):");
	scanf("%d",&dist);
	total= dist;  //总行驶的里程 
	j= 1;  //选择的加油站个数 
	while(total<S)  //如果汽车未走完全程
	{
		for(i=k; i<n; i++)
		{
			if(x[i]>total)  //如果距离下一个加油站太远
			{
				a[j]= x[i-1];  //则在当前加油站加油
				j++;
				total= x[i-1]+dist;  //计算加完油能行驶的最远距离
				k= i;
				break; 
			 } 
		}
	} 
	for(i=1; i<j; i++)  //输出加油点 
		printf("%4d",a[i]); 
} 

猜你喜欢

转载自blog.csdn.net/m0_63794226/article/details/127505382