什么样的问题适合使用穷举法来解决呢?归纳起来,遇到了如下的三种情况,将优先考虑使用穷举法:
1. 答案的范围已知:
虽然事先并不知道确切的结果,但能预计到结果会落在哪个取值范围内。譬如说:
①求1-100之间所有的素数: 无论结果如何,都在1-100的范围之内。
②求2000-2015年间有几个月的13号是周日?这15年间共有180个月,月份的个数最多不会超过180
③验证1000以内的哥德巴赫猜想:即找出1000之内所有的合数,看是否能够分解为两个质数之和
。。。。。。
如果仔细观察,将会发现许多题目的结果范围都是已知的,都可以使用穷举法来实现。
2. 答案的结果是离散的,不是连续的。如果要求出1-2之间所有的小数,就无法用穷举法来实现,因为其结果是无限连续的。
3. 对时间上的要求不严格。蓝桥杯比赛中的许多题目对于算法的设计是有时间要求的,有时会非常苛刻。如果用穷举法则耗时过长,不可取。例如求出21位的水仙花数,使用穷举法可能会花费30分钟的时间。而蓝桥杯试题通常要求时间限制在1秒钟之内完成,少数会延长至3分钟。在这种情况下,必须使用新的算法来解决问题。
下面举个经典的例子:
100块砖100人来搬,男人一人搬4块,女人一人搬3块,小孩3人抬一块,问男,女,小孩各几人?
若设男,女,小孩人数分别为X, Y, Z,则只能够列出两个等式: X+Y+Z=100 4*X+3*Y+Z/3=100 。三个未知数两个等式,无法求解。这就只能够使用穷举法来实现,具体做法如下:
先确定每种类型人员的数量的取值范围,由题意可知,男人X的取值范围是0~100/4=25 女人Y的取值范围是0~100/3=33 小孩的取值范围是0~99(必须不大于100且为3的倍数)。使用穷举法遍历所有可能的取值结果,逐一判断筛选出正确的结果。编程如下:
for(int x=0; x<=25; x++)
for(int y=0; y<=33; y++)
for(int z=0; z<=99; z+=3)
if((x+y+z==100)&&(4*x+3*y+z/3==100))
{输出找到的结果}
如果仔细分析一下,就会发现由于x+y+z==100,那么只需要考虑x和y的遍历取值;z值可以通过100-x - y来实现。当然,z值是3的倍数,上述代码可修改如下:
for(int x=0; x<=25; x++)
for(int y=0; y<=33; y++)
{
int z = 100 - x - y;
if((z%3==0)&&(4*x+3*y+z/3==100))
{输出找到的结果}
}
从这道题的解决过程中,我们可以发现使用穷举法的一般过程:
确定需要哪几个变量,此题需要3个变量x,y, z 如果是其它的题目,所需变量的个数与类型有可能不尽相同,这个要由具体情况而定。
确定每个变量的取值范围,如上例之中X的范围是0~25, y的取值范围是0~33, z的取值范围是0~33
设置多层的嵌套循环,通常为for循环,最内层之中设置条件判断,满足输出条件时,输出相关的提示信息。
再来一道可以使用穷举法解决的问题:
有一群海盗(不多于20人),在船上比拼酒量。过程如下:打开一瓶酒,所有在场的人平分喝下,有几个人倒下了。再打开一瓶酒平分,又有倒下的,再次重复...... 直到开了第4瓶酒,坐着的已经所剩无几,海盗船长也在其中。当第4瓶酒平分喝下后,大家都倒下了。 等船长醒来,发现海盗船搁浅了。他在航海日志中写到:“......昨天,我正好喝了一瓶.......奉劝大家,开船不喝酒,喝酒别开船......”
请你根据这些信息,推断开始有多少人,每一轮喝下来还剩多少人。 如果有多个可能的答案,请列出所有答案,每个答案占一行。格式是:人数,人数,...
例如,有一种可能是:20,5,4,2,0
蓝桥杯中的许多题目都会提供一个答案的示例,通过分析这个示例可以加深对当前这道题的理解。以本题为例,通过研究示例答案可以看出如下的特点:
(1)答案只可能有4个数,分别代表每轮参与的人数,最后的0是固定的。
(2)每轮的人数是不断减少的,下一轮只能够比上一轮人数更少。
(3)每轮人数被1除之后,累加和是1(刚好一瓶酒)
即 1/20+1/5+1/4+1/2=1
根据上述的分析,可以确定如下的变量s1, s2, s3, s4分别代表每轮的人数。
这四个变量的取值范围分别是 1≤s1≤20 1≤s2≤s1 1≤s3≤s2 1≤s4≤s3 据此来设置4层的嵌套循环,并加上最内层的条件判断如下:
for(int s1=1; s1<=20; s1++)
for(int s1=1; s1<=20; s1++)
for(int s1=1; s1<=20; s1++)
for(int s1=1; s1<=20; s1++)
if(1/s1+1/s2+1/s3+1/s4==1){输出结果}
理论上来看是正确的,但实际运行时,由于/运算符在左右两边均为整数时,表示整除而不是除法,所以上述的条件判断需要改写为更复杂的形式:
(s1*s2*s3+s1*s3*s4+s1*s3*s4+s2*s3*s4)/(s1*s2*s3*s4)==1
根据笔者指导蓝桥杯的经验,至少有三分之一的问题都可以通过穷举的方法来实现,因此,把穷举法练习使用好,意义非常重大。
作业:
(1) 换分币: 将5元的人民币兑换成1元,5角和1角的硬币,共有多少种不同的兑换方法?
(2) 找完数: 求1~100内完数的个数。 如果一个数等于它的因子之和,则称该数为完数(或“完全数”)。例如,6的因子为1,2, 3 而6=1+2+3 , 因此6是完数
(3)找自守数:自守数是指一个数的平方的尾数等于该数自身的自然数。例如:5*5=25; 25*25=625; 76*76=5776; 9376*9376=87909376, 求100000以内的自守数。