贪心算法实例讲解(2)

来源:互联网 发布:js定义类的对象和方法 编辑:程序博客网 时间:2024/05/02 00:37

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

贪婪算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。

对于根据题意选取一种量度标准,比如钓鱼问题,选取的量度标准是:每个湖的余量,然后按照大小进行排序,依次选取当前最多的湖。又如喷水装置,选取的量度标准是:半径的大小,按照半径大小依次排序,依次选取半径最大的情况。

然而选取量度标准是个不容易的事情。一般都是先简历一个数学模型,然后再深入分析,是否符合用贪心法。

钓鱼问题:

黑书中的经典题:枚举+贪心
把每钓5分钟鱼称为钓一次鱼。首先枚举John需要走过的池塘的数目X,即从池塘1走到池塘X。减去路上花去的时间T=sum(Ti) i=1...X-1,这样我们可以认为John能从一个池塘"瞬间转移"到另一个池塘,即在任意一个时刻都可以在池塘1到池塘X中任选一个钓一次鱼(很重要)。现在采用贪心策略,每次选择鱼最多的池塘钓一次鱼。对于每个池塘来说,由于在任何时候鱼的数目只和John在该池塘里钓鱼的次数有关,和钓鱼的总次数无关,所以这个策略是最优的。假设一共允许钓k次鱼,那么每次在N个池塘中选择鱼最多的一个钓。总的时间复杂度为O(kn^2)。(黑书中的解释)

 在最后的结果中,第一个最大值所对应的在每个池塘的钓鱼时间为题目要求的情况,因为如果John在后面的池塘钓了鱼,那么前面相应的时间就会减少。最后注意池塘中没有鱼的情况。

结题步骤如下:

1.在枚举了走过的池塘数目下,分别计算(总时间s-花在路上的时间t)/5=times就是钓鱼的次数。

2.首先选取当前池塘鱼数最多的池塘,然后可掉鱼数max+鱼数,然后次数减一,当池塘中的鱼<0时赋值为0,并退出本次枚举,或者次数为0时,退出本次枚举,然后记录鱼数。如果还有次数,但是没有鱼了,把剩余次数加在第一个池塘。

 

  1. #include <iostream>
  2. using namespace std;
  3. //f[]为每个池塘的鱼数,d[]为每次减少的条数,t[]为路上花费的时间
  4. int n,h,f[30],f1[30],d[30],t[30];
  5. //用来查找当前鱼数最多的池塘
  6. int chazhao(int n)
  7. {
  8. int i,falg=1;
  9. for(i=1;i<=n;i++)
  10. if(f[falg]<f[i])
  11. {
  12. falg=i;
  13. }
  14. return falg;
  15. }

  16. int main()
  17. {

  18.    int i,j,max,Max,min,falg,time,times;
  19.    int count[30],count1[30];//记录每个湖钓的次数
  20.    cout<<"输入总的池塘数"<<endl;
  21.    while(scanf("%d",&n)!=EOF&&n)
  22. {
  23.     cout<<"输入小时:"<<endl;
  24.     scanf("%d",&h);
  25.     Max = -1;
  26.     memset(count1,0,sizeof(count1));
  27.     memset(f,0,sizeof(f));
  28.     memset(d,0,sizeof(d));
  29.     memset(t,0,sizeof(t)); //数据的输入
  30.     cout<<"输入每个池塘的鱼数"<<endl;
  31.     for(i=1;i<=n;i++)
  32. {
  33.     scanf("%d",&f[i]);
  34.     f1[i] = f[i]; //f1用来保存每个鱼塘的鱼数么???
  35. }
  36. cout<<"输入每个池塘的减少的鱼数"<<endl;
  37. for(i=1;i<=n;i++)
  38. scanf("%d",&d[i]);
  39. cout<<"输入每个池塘的行走时间"<<endl;
  40. for(i=1;i<n;i++)
  41. scanf("%d",&t[i]);
  42. for(i=1;i<=n;i++)//依次枚举贪心
  43. {
  44.    time = 0;//花在路上的总时间
  45.    max = 0;
  46.    memset(count,0,sizeof(count));
  47.    for(j=1;j<=i;j++)
  48.      time+=t[j-1]*5;
  49.    times = (* 60 - time)/5;//可钓鱼的次数
  50.    if(times <=0)//如果本次钓鱼次数为0,则结束本次枚举
  51.       continue;
  52.    while(1)
  53. {
  54.     falg = chazhao(i);//找出本次枚举中,每次选择当前鱼数最多的池塘。
  55.     if(f[falg] <= 0)//如果最多池塘的鱼数也小于等于0了,则也同样跳出本次枚举。???
  56.     break;
  57.     max+=f[falg];//这个是在本次枚举中,每选择一次就要把钓到的鱼加起来。
  58.     count[falg]++;//记录每个池塘钓鱼的次数
  59.     times--;
  60.     f[falg]-=d[falg];
  61.     if(f[falg]<0)
  62.       f[falg] = 0;
  63.     if(times == 0)//如果次数等于0了,则跳出本次枚举
  64.     break;
  65. }
  66. if(times)//这里是因为,还有可以钓鱼的次数,但是因为池塘没有鱼了。
  67. {
  68. /* min = 1;
  69. for(= 2;j<=i;j++)
  70. if(count[min] < count[j])
  71. min = j;
  72. count[min]+=times;*/
  73. count[1]+=times;//就把所有的次数都加到
  74. }

  75. if(Max < max)
  76. {
  77.   Max = max;
  78.   for(j=1;j<=i;j++)
  79.   count1[j] = count[j];//把每个池塘钓鱼的次数赋值给count1.
  80. }
  81.   for(j=1;j<=n;j++)
  82.   f[j] = f1[j];//f1[]数组保存的是最原始的每个池塘的鱼数,这样每次枚举必须用的是初始值。
  83. }
  84. for(i=1;i<n;i++)
  85. printf("%d, ",count1[i]*5);
  86. printf("%d\n",count1[n]*5);
  87. printf("%s%d\n\n","Number of fish expected: ",Max);

  88. }
  89. return 0;
  90. }

 

喷水装置

关于喷水装置这个题,我当时不懂的地方是,长方形的中心线在哪?嘿嘿是中线的位置。

【解题思路】:由贪心算法,优先使用半径大的喷水装置

【核心算法】:

    

  1. sum=count=0; //初始化

  2.         cin>>n; //输入喷水装置总数

  3.         for(i=0;i<n;i++) cin>>r[i]; //输入

  4.         sort(r,r+n); //对半径升序排序

  5.         for(i=n-1;i>=0;i--) //找出最少装置数

  6. {

  7.            sum+= sqrt(r[i]*r[i]-1); // sum+= sqrt(r[i]*r[i]-1)*2;

  8.                                  // sqrt(r[i]*r[i]-1)*2 :装置覆盖矩形区域的长度 

  9.            count++; //记录喷水装置数

  10.            if(sum>=10) break; // if(sum>=20) break; 


  11. } 

  12.         cout<< count<<endl; //输出最少装置数

 

潜水比赛

这个题我是栽了,当时还在想,是那一队N个人一起潜水,在水中的时候,轮流使用氧气瓶呢(人可以闭气么),但是人家都说了,潜水时必须使用氧气瓶。所以这个题跟过桥题一样,都是贪心法。

都是依照时间进行排序,然后选取最快的一对走,然后在另一头选取最快的拿回氧气瓶。

 

 

0 0
原创粉丝点击