原题目地址: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为整数
这个题目可以试着解一下
未完待续)