贪心算法

来源:互联网 发布:淘宝网开通借呗可靠吗 编辑:程序博客网 时间:2024/06/17 04:13

1.基本思想

对于许多最优化问题,动态规划并不是唯一的选择,通常贪心算法更简单、更高效,贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。

2.基本要素

1.贪心选择性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
2. 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

Tips:贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题

3.贪心算法的实现

* 设计贪心算法的几个基本步骤*

1.确定问题的最优子结构2.一个递归算法(递归式)3.证明做出一个贪心选择后,只剩下一个子问题。4.设计递归算法的贪心策略5.将递归算法转化为迭代算法

最后转化成的迭代算法形式为:

从问题的某一初始解出发;while 能朝给定总目标前进一步 do   求出可行解的一个解元素;由所有解元素组合成问题的一个可行解;

4.若干经典问题

1)活动时间安排的问题
设有N个活动时间集合,每个活动都要使用同一个资源,比如说会议场,而且同一时间内只能有一个活动使用,每个活动都有一个使用活动的开始si和结束时间fi,即他的使用区间为(si,fi),现在要求你分配活动占用时间表,即哪些活动占用该会议室,哪些不占用,使得他们不冲突,要求是尽可能多的使参加的活动最大化,即所占时间区间最大化!
这里写图片描述
贪心策略:将活动按照结束时间进行从小到大排序。然后用i代表第i个活动,s[i]代表第i个活动开始时间,f[i]代表第i个活动的结束时间。按照从小到大排序,挑选出结束时间尽量早的活动,并且满足后一个活动的起始时间晚于前一个活动的结束时间,全部找出这些活动就是最大的相容活动子集合

    #include <iostream>      using namespace std;      void GreedyChoose(int len,int *s,int *f,bool *flag);      int main(int argc, char* argv[])      {          int s[11] ={1,3,0,5,3,5,6,8,8,2,12};          int f[11] ={4,5,6,7,8,9,10,11,12,13,14};          bool mark[11] = {0};          GreedyChoose(11,s,f,mark);          for(int i=0;i<11;i++)              if(mark[i])                  cout<<i<<" ";          system("pause");          return 0;      }      void GreedyChoose(int len,int *s,int *f,bool *flag)      {          flag[0] = true;          int j = 0;          for(int i=1;i<len;++i)              if(s[i] >= f[j])  //检查活动i是否与当前已选择的所有活动相容。若相容活动i加入已选择活动的集合中            {                  flag[i] = true;                  j = i;              }      }  

2)背包问题:给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,1≤i≤n。
贪心策略:

i) 一种贪婪准则为:从剩余的物品中,选出可以装入背包的价值最大的物品,利用这种规则,价值最大的物品首先被装入(假设有足够容量),然后是下一个价值最大的物品,如此继续下去。这种策略不能保证得到最优解。例如,考虑n=2, w=[100,10,10], p =[20,15,15], c = 105。当利用价值贪婪准则时,获得的解为x= [ 1 , 0 , 0 ],这种方案的总价值为2 0。而最优解为[ 0 , 1 , 1 ],其总价值为3 0。(ii) 另一种方案是重量贪婪准则是:从剩下的物品中选择可装入背包的重量最小的物品。虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。考虑n= 2 ,w=[10,20], p=[5,100], c= 2 5。当利用重量贪婪策略时,获得的解为x =[1,0], 比最优解[ 0 , 1 ]要差。(iii) 还有一种贪婪准则,就是我们教材上提到的,认为,每一项计算yi=vi/si,即该项值和大小的比,再按比值的降序来排序,从第一项开始装背包,然后是第二项,依次类推,尽可能的多放,直到装满背包。有的参考资料也称为价值密度pi/wi贪婪算法。这种策略也不能保证得到最优解。利用此策略试解n= 3 ,w=[20,15,15], p=[40,25,25], c=30 时的最优解。虽然按pi /wi 非递(增)减的次序装入物品不能保证得到最优解,但它是一个直觉上近似的解。而且这是解决普通背包问题的最优解,因为在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,1≤i≤n。
void  Knapsack(int n,float C,float V[],float W[],float x[]){ Sort(n,V,W)//以每种物品单位重量价值从大到小排列 for(int i = 1; i <= n;++i) x[i] = 0; float c = M; for(int i = 1;i <= n;++i){    if(w[i]>c)       break;     x[i] = 1;     c -= w[i];}if(i<= n)  //若此时没有装满,则放入部分商品   x[i] = c/w[i] }

3)最优装载问题:有一批集装箱要装上一艘载重量为c的轮船。其中集装箱i的重量为Wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
贪心策略:采用重量最轻者先装的贪心选择策略,可产生最优装载问题的最优解。

void Loading(int x[], Type w[],Type c,int n){    Sort(w,n); //将w从小到大排序    for(int i = 1;i <= n;++i) x[i] = 0;    for(int i = 1;i <= n && w[i] <= c; ++i){        x[i] = 1;         c -=w[j];    }}

4)多机调度问题:设有n个独立的作业{1, 2, …, n}, 由m台相同的机器进行加工处理. 作业i所需时间为t i. 约定:任何作业可以在任何一台机器上加工处理, 但未完工前不允许中断处理,任何作业不能拆分成更小的子作业。要求给出一种作业调度方案,使所给的n 个作业在尽可能短的时间内由m台机器加工处理完成。(NP完全性的问题)

tips :多机调度问题是一个NP完全问题,到目前为止还没有完全有效的解法,贪心选择策略有时可以设计出一个比较好的近似算法。

贪心策略:采用最长处理时间作业优先的贪心策略:
当n≤m时, 只要将机器i的[0, ti]时间区间分配给作业i即可。
当n>m时, 将n个作业依其所需的处理时间从大到小排序,然后依次将作业分配给空闲的处理机。

    Sort(time,n);//将n个作业按时间从大到小排序。    if(m >= n)}

5)找零钱问题
一个小孩买了价值为33美分的糖,并将1美元的钱交给售货员。售货员希望用数目最少的硬币找给小孩。假设提供了数目有限的面值为25美分、10美分、5美分、及1美分的硬币。给出一种找零钱的贪心算法。

贪心策略:每次找钱都找尽量大面值的钱,但要满足找回的钱的总数不大于需要找回的钱。

void Comput(int price, int money, int result[])  {      int i = 0;      int changes = money - price;  //需要找回的钱数    while (changes !=0 )      {          if (changes >= 100)          {              result[i++] = 100;              changes -= 100;          }          else if (changes >= 20)          {              result[i++] = 20;              changes -= 20;          }          else if (changes >= 10)          {              result[i++] = 10;              changes -= 10;          }          else if (changes >= 5)          {              result[i++] = 5;              changes -= 5;          }          else          {              result[i++] = 1;              changes -= 1;          }      }  }
0 0
原创粉丝点击