努力是为了不平庸~
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。
学习日记
目录
一、贪心算法
贪心算法(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]);
}