原題目地址:http://www.cnblogs.com/DeanChopper/p/4772593.html
大廳里有100盞燈,每盞燈都編了號碼,分別為1-100。每盞燈由一個開關來控制。(開關按一下,燈亮,再按一下燈滅。開關的編號與被控制的燈相同。)開始時,燈是全滅的。現在按照以下規則按動開關。
第一次,將所有的燈點亮。
第二次,將所有2的倍數的開關按一下。
第三次,將所有3的倍數的開關按一下。
以此類推。第N次,將所有N的倍數的開關按一下。
問第100次按完以后,大廳里還有幾盞燈是亮的。
網上找了一下,此題還有其他的表述方式類似:
禮堂里有100盞電燈,編號1-100,每盞燈由一根燈繩控制,拉一下改變狀態.開始燈全是滅的,100個同學依次進入禮堂,
第一個學生把1的倍數的燈繩拉一下,燈全亮;
接着第二個同學把2倍數的燈繩拉一下;
第三個同學把3倍數的燈繩拉一下······第100個學生把100的倍數的燈繩拉一下.最后,禮堂里哪些燈是亮的?
1,暴力解法:
暴力解法一般是思路上最接近題意的,按題目要求的步驟直接求解,暴力求解一般計算量也是最大的。
我一般都是先用笨辦法計算出結果,再想辦法優化。現給出C#暴力解法,得出最后的結果,代碼如下:
1 class Program { 2 3 //100盞燈,默認全是關着的 4 static bool[] lights = new bool[100]; 5 6 static void Main(string[] args) { 7 //100次開關測試 8 for (int i = 1; i <= 100; i++) { 9 ChangeState(i); 10 } 11 12 //開關完成,打印最后的結果 13 Console.WriteLine("最后亮燈的還有{0}盞。", lights.Count(r => r)); 14 15 //分別打印每盞燈 16 for (int i = 0; i < 100; i++) { 17 if (lights[i]) { 18 Console.WriteLine(i + 1); 19 } 20 } 21 22 Console.WriteLine("實驗結束!"); 23 Console.ReadKey(); 24 } 25 26 /// <summary> 27 /// 開關燈 28 /// </summary> 29 /// <param name="x"></param> 30 private static void ChangeState(int x) { 31 32 for (int i = 0; i < 100; i++) { 33 if ((i + 1) % x == 0) { 34 lights[i] = !lights[i]; 35 } 36 } 37 38 } 39 40 }
上面的解法很容易就能求出結果,各位可以先自行測試一下。
2.優化
暴力解法可以得到結果,但這肯定是不能讓人滿意的。現在回過頭來再分析這個題。
第一次操作,每個燈的開關都被按一次;
第二次操作,2的倍數的開關都被按一次;
.....
下面列出前十次,前十個數的具體情況
燈1 | 燈2 | 燈3 | 燈4 | 燈5 | 燈6 | 燈7 | 燈8 | 燈9 | 燈10 | |
步驟1 | 操作 | 操作 | 操作 | 操作 | 操作 | 操作 | 操作 | 操作 | 操作 | 操作 |
步驟2 | 操作 | 操作 | 操作 | 操作 | 操作 | |||||
步驟3 | 操作 | 操作 | 操作 | |||||||
步驟4 | 操作 | 操作 | ||||||||
步驟5 | 操作 | 操作 | ||||||||
步驟6 | 操作 | |||||||||
步驟7 | 操作 | |||||||||
步驟8 | 操作 | |||||||||
步驟9 | 操作 | |||||||||
步驟10 | 操作 | |||||||||
操作次數 | 1 | 2 | 2 | 3 | 2 | 4 | 2 | 4 | 3 | 4 |
可以看到,最下面一列是操作的次數,操作次數為奇數次的,燈最后是打開的狀態,是偶數次,則為關閉。
被操作的規律如下:
燈1:1
燈2:1,2
燈3:1,3
燈4:1,2,4
...
依次寫下去,就可以發現,每個燈被操作的步驟,其實為此燈編號的因數。
如燈36被操作的步驟:1,2,3,4,6,9,12,18,36
此時題目轉化為求[1-100]100個數中,因數個數為奇數的數的數量是多少。
因為最近在學習Python,求因數的算法用python 語言給出,3.4 版本運行成功 如下:
1 def getZYS(n): 2 x=2 3 zys=[] 4 zys.extend([1,n]) 5 while x*x<=n: 6 if n%x==0: 7 if x*x==n: 8 zys.extend([x]) 9 else: 10 zys.extend([x,n//x]) 11 x+=1 12 return zys 13 14 n=input('請輸入一個正整數:') 15 n=int(n) 16 lb=getZYS(n) 17 lb=sorted(lb) 18 print(lb) 19
此時,題目的解法已經第一次被推廣,在一定程度上,我們已經可以解決更多類似的問題。推廣后的解法請自行試着去解決。
3.就事論事
求一個正整數的質數的過程,如上面代碼所示,其實就是對某個數平方根以下的數試着求余,但具體到本題而言,轉化一下描述為,怎樣快速判斷一個數的因數個數是否為奇數。
請看上面的代碼,第7-10行,如果能深刻理解這幾行,上面所述的題目會有更快的解法。
我們注意到,求一個數因數的過程,是從1開始到其平方根,除平方根外,其他的因數都是成對的出現!,也就是,只有平方數的因數是奇數個!,100以內的平方數只有10個,分別是1,4,9,16,25,36,49,64,81,100,所以上述題目的最后結果是10個燈最后是亮着的。
優化完的代碼,注釋要多一些,代碼本身將非常之少,因為100的平方根是10,所以最后亮10盞燈。
你明白了沒有?
附:
求一個正整數的因數的個數的研究,最初來源於一個Python群的入群題目:
求1<=i<=10**12范圍內所有d(i)的和的末12位,d(i)表示i的正約數的和,i為整數
這個題目可以試着解一下
未完待續)