常用算法-窮舉法
窮舉法又稱為枚舉法,它是在計算機算法設計中用得最多的一種編程思想。它的實現方式是:在已知答案範圍的情況下,依次地枚舉該範圍內所有的取值,並對每個取值進行考查,確定是否滿足條件。經過循環遍歷之後,篩選出符合要求的結果來。這種方法充分利用了計算機運算速度快的特點,思路簡單直接,能夠解決大部分的問題。
什麽樣的問題適合使用窮舉法來解決呢?歸納起來,遇到了如下的三種情況,將優先考慮使用窮舉法:
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以內的自守數。
鏈接:https://blog.csdn.net/luojin2003529/article/details/49516935
常用算法-窮舉法