“蓝桥杯”枚举法(一)——百钱买百鸡、等差素数数列

1. 枚举法

枚举法又称暴力算法,是指逐个考查某类事件的所有可能情况,进而得出一般结论的方法。枚举法的思想是将问题所有可能的答案逐个列举,然后根据条件判断此答案是否满足,保留满足的,舍弃不满足的。
枚举法比较直观,算法也很容易理解,但枚举法在实际使用中应该尽量减少变量的个数,以及搜索的空间,这样算法的效率才能提高。

2.百钱买百鸡

我国古代数学家张丘建在《算经》一书中曾提出过著名的“百钱买百鸡”问题,该问题的叙述如下:
鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则翁、母、雏各几何?
本题的数据规模比较小,利用现代计算机的算法可以直接枚举,由于公鸡、母鸡和小鸡的数量都在0~100只,因此可以直接枚举整个空间。案例代码如下:

for(i=0; i<=100;i++)
   for(j=0;j<=100;j++ )
     for(k=0; k<=100;k+ +)
      if(5* i+3* j+k/3==100 && k%3==0 && i+j+k==100)
      printf("公鸡号2d只,母鸡号2d只,小鸡号2d只\n",i,j,k);

如果本题的已知条件不变,将数据规模变大,。变成“万钱买万鸡”的问题,则请你计算一下总共有多少种买法。
这时需要通过变换以减少搜索的空间。
本题中,有3个变量i,j,k,其实只要任何两个变量的值确定后,另一个变量的值就已经确定了。例如: i=1,j=2,这时k只能等于100-1-2=97才满足要求,所以通过变量之间的关系就可以减少搜索空间。
通过分析可知,,小鸡的变量k不需要搜索整个空间,因为要求k的值必须是3的倍数才能满足条件,这样就可以进一步减少搜空间,于是“万钱买万鸡”的问题可以如下解决。

int count=0;
for(-i=0;i<=10000;i++)
   for(k=0;k<=10000;k=k+3)
   {
    j=10000-i-k;
    1f(j< 0) continue;
    1f(5* i+3* j+k/3==10000)
   count++;
  }
printf("%d", count);

如果本题的规模进一步扩大到“百万钱买百万鸡”,那利用上述算法就会超时,需要进步缩小枚举的规模。
通过题目分析可知,要想用定数量的钱买到同等数量的鸡,小鸡必不可少,(因为只有有小鸡,数量才能达到平衡),并且是3的倍数。通过分析可知:
三只小鸡 + 一只母鸡=四只鸡,而这四只鸡是四文钱,刚好达到平衡。
六只小鸡+ 一只公鸡=七只鸡,而这四只鸡是七文钱,刚好达到平衡。
也就是说,要想用一定数量的钱买到同等数量的鸡,只有这两种组方式能达到平衡;
三只小鸡 + 一只母鸡........①
六只小鸡 + 一只公鸡.........②
本问题就变成了求4x+7y=1000000这个方程的解空间的数量(x代表①组的数量,y代表②组的数量)。这个方程中,4 是1000000的因子,7是一个质数,很容易地就能得出解空间的规律为:
x                   y
250000      0
249993      4
249986       8
249979      12
249972      16
249965     20 ……
这个解空间,即母鸡组每次减少7组,公鸡组每次增加4组就可以达到平衡,所以程序可以进一步简化为:

int count=0;
for(x= 250000;x>=0;  x=x-7)
count+ + ;
printf("id", count) ;

进一步简化为
printf("ad", 1000000/28+1) ;
这个式子请同学们自己分析和思考一下。
通过本题可以看出,使用枚举法一方面是非常灵活的,另一方面,根据问题的规模需要探索不同的算法,这也是算法竞赛经常考查考生的地方,大家在后面的学习过程中要学会分析问题的规模。
在大多数算法竞赛测试平台上,每秒的操作次数约为1e7,在这个限制下时间复杂度一定的算法存在数据规模的处理上限,具体的时间复杂度和数据规模上限如下表所示。

 3. 等差素数数列

【问题描述】
2,3,5,7,11,13,..是素数数列。
7,37,67,97,127.157.这样完全由素数组成的等差数列称为等差素数列。
上述数列的公差为30,长度为6。
2004年。格林与陶哲轩合作证明了存在任意长度的等差素数列。这是数论领域的一项惊人成果!
有了这理论作为基础,请你借助手中的计算机满怀信心地搜索:长度为10的等差素数列,其公差最小值是多少?
注意:需要提交一个整数,不要填写任何多余内容和说明文字。

[提示]

本题是关于素数的题目,所以第一步就是要学会判断素数的算法。常用的素数判断算法有两种:一种是基于素数定义的枚举法,另一种是筛选法。
基于素数定义的枚举法的思想非常简单,要想判断n是否是素数,只需要从2到n-1枚举是否有数能够被n整除即可。该方法不再赘述,这里重点介绍筛选法。
(1)筛选法
筛选法非常适合求一个整数区间中各数是否是素数的情况,并且区间越大,效率越高。筛选法据说是由古希腊的埃拉托斯特尼(Eratosthenes,约公元前274-公元前194年)发明的,,因此又称之为埃拉托斯特尼筛子。
具体做法是:先把N个自然数按次序排列起来,因为1不是质数,也不是合数,所以将1划去,从2开始。
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22……N
第二个数2是质数,保留下来,把2后面所有能被2整除的数都划去。
2 3 5 7 9 11 13 15 17 19 21 ……N
这时,2后面第一个没被划去的数是 3,把3留下,再把3后面所有能被3整除的数都划去。
2 3 5 7 11 13 17 19 ……N
3后面第一个没被划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。
这样一直做下去,就会把不超过N的全部合数都筛掉,留下的数就是不超过N的全部质数。
因为希腊人是把数写在涂蜡的板上的,每划去一个数,就在上面记一个小点,寻求质数的工作完毕后,许多的小点就像一个筛子,所以就把埃拉托斯特尼的方法叫作“埃拉托斯特尼筛子”,简称“筛选法”。
(2)程序的思路
当判断出2~N中的所有素数后,下面便可以采用暴力算法进行枚举。目前有3个不确定的变量:N的范围、公差和素数序列的起始值。
N的范围可以设定成一个常量,需要足够大,以便满足要找的序列。
公差和素数序列的起始值分别设定为两个变量,采用枚举法进行两层循环。循环过程中,判断是否有满足长度为10的等差素数列,如果有,则输出其公差,即是公差最小值。

等差素数数列(答案)

#include <iostream>
#include <cmath> 
using namespace std;
const int N=10000; 
int a[N+1];
//筛选法找2~N之间的素数 
void primes()
{
 for(int i=2;i<=N;i++) //全部置1 
  a[i]=1;  //用1表示是数,0表示不是素数
 for(int i=2;i<=N;i++)
 {
  if(a[i]) // 如果a[i]是素数 
  {
   for(int j=2*i;j<=N;j=j+i) // a[i]的倍速不是素数 
   a[j]=0;
  }
  } 
}
//找从素整数n开始,满足差值是m的10个连续素数
bool isOK(int n,int  m)  
{
 for(int i=1;i<10;i++)
 {
  if(!a[n]) return false; //如果不是素数,返回false 
  else n=n+m; //否则看差值为m的下一个数是否为素数 
 } 
 return true;//满足条件 
 } 
int main()
{
 primes(); //筛选法找到所有的素数 
 //从差值为2开始找,有没有差值为2的10个连续的素数
 //如果没有,那么看差值是3,有没有差值是3的10个连续素数……
 /*for(int i=2;i<=10000;i++) //输出所有的素数 
 if(a[i])
   cout<<i<<endl;
 */
 int cha=2;
 while(1 ) 
 {
  for(int i=2;i<=N;i++)
  {
   //i是素数,并且差值是cha的10个素数 
   if(a[i]&&isOK(i,cha)) 
   {
    cout<<cha<<endl;
    return 0; 
    } 
  }
  cha++;//差值加1,继续找 
 } 
 return 0;
 }

免费领取300G精品编程资料icon-default.png?t=N2N8https://jq.qq.com/?_wv=1027&k=s6zhEe1L

猜你喜欢

转载自blog.csdn.net/yx5666/article/details/129817788