m个人拉m盏灯后求灯的状态问题

之前看过一道题:有m盏灯,编号分别为1,2,3,...,m,每拉一次灯的开关,灯的亮灭状态就发生一次变化。这m盏灯初始状态都是亮着的,有m个人去拉灯,第1个人把所有的灯都拉一次,第2个人把编号是2的倍数的灯拉都拉一次,第3个人把编号是3的倍数的灯都拉一次,...,第m个人把编号是m的灯都拉一次。问:最终哪些编号的灯是灭的?

以下是解决这种问题的几种方法。

目录

一、人工按步骤操作

二、编程求解方法

1、编程方法一:最基本编程求解思路

2、编程方法二:数理分析+枚举法

3、编程方法三:求m中最大平方根法


一、人工按步骤操作

如果m的数值比较小,可以用手动的方法一个个去试一下,也是可以做出来的,如下表所示。

灯1 灯2 灯3 灯4 灯5 灯6 灯7 灯8 灯9 灯10
初始
拉1次
拉2次
拉3次
拉4次
拉5次
拉6次
拉7次
拉8次
拉9次
拉10次

但这种方法效率太低了,而且容易出错,如果m值很大,用这种手动的方法就更不可行了。

二、编程求解方法

1、编程方法一:最基本编程求解思路

可以用编程的方法来解决,这个问题如果用c++语言编程的思路如下:

第一步:声明一个整形变量m,由键盘输入m的具体值。

第二步:声明一个有m+1个元素的布尔型数组,其中脚标为1到m的元素用于存储m盏灯的亮灭状态,亮为1,灭为0。

第三步:用for循环求解当所有人都拉过一次灯后,灯的状态。这一步是关键,灯的序号用i表示,人的序号用j表示,从i=1开始到m结束,循环求解j=1开始到m结束,i是否是j的倍数,如果是则序号为i的灯状态翻转一次。

第四部:把状态为灭的灯的序号输出。

具体代码如下:

#include <iostream>
using namespace std;
 
int main()
{
    int m;    //声明m盏灯
    cin>>m;    //键盘输入m的具体数值    
    bool lamp_status[m+1];    //声明存储灯状态的布尔型变量数组,1为亮,0为灭

    for(int i=1;i<=m;i++)    //循环求解i盏灯的状态
        {
            lamp_status[i]=1;    //灯的初始状态为亮
            for(int j=1;j<=m;j++)    //循环求解第j个人拉灯之后,灯的状态变化
                {
                    if(i%j==0)        //如果i是j的倍数,则灯的状态进行翻转
                    lamp_status[i]=!lamp_status[i];
                }
                
            if(lamp_status[i]==0)     //如果灯的状态为灭则输出灯的序号
                {
                    cout<<i<<endl;
                }
        } 
    return 0;
}

运行程序,当m=10时,结果如下:

1
4
9

当m=100时,结果如下:

1
4
9
16
25
36
49
64
81
100

运算结果与题目给出的答案是一致的,编程求解的算法成功。

2、编程方法二:数理分析+枚举法

但本文目的不仅在此。

通过观察编程方法一运行的结果发现,灯状态为灭的序号都是完全平方数!为了排除巧合,把m的值再加大到1000,10000,得到的所有结果仍然是完全平方数。也就是说,这不是偶然的现象,而是一个必然的规律。

那么这个规律是什么呢?我们从数学角度来分析一下。

用i来表示从1到m的某盏灯的编号,j表示从1到m的某个拉灯的顺序号。

第i盏灯的最终亮灭状态是由灯的状态翻转次数决定的,当翻转次数是奇数次,那么灯的状态与初始状态相反,为灭;当翻转次数为偶数次,灯的状态与初始状态相同,为亮。

第j个人拉灯时,当i是j的倍数,或者说j是i的因数时,第i盏灯状态才发生翻转。而j的最大值和i的最大值都为m,那么第i盏灯翻转的次数等于i的因数的个数。

所以求解第i盏灯的状态问题,就转化为求解i这个自然数含有因数的个数是奇数还是偶数的问题。那么一个自然数,它的因数的个数有什么规律呢?这个就跟这个自然数是不是完全平方数有关,我们知道一个自然数总可以表示成另外两个自然数相乘的形式,这两个自然数就是它的因数。

比如:1=1×1,2=1×2;4=1×4=2×2;5=1×5;6=1×6=2×3,7=1×7,8=1×8=2×4,12=1×12=2×6=3×4,36=1×36=2×18=3×12=4×9=6×6...。

通过观察发现,一个自然数的因数都是成对出现的,当存在某对因数相等的时候,这个自然数就是完全平方数,也就是完全平方数的因数的个数是奇数而非完全平方数,不存在某对因数相等的情况,所以因数的个数为偶数

至此,判断第i盏灯状态是否是亮灭的时候,就转化为看i是否为完全平方数的问题了,当i为完全平方数,其因数个数为奇数个,第i盏灯状态与初始状态相反,为灭;当i不是完全平方数时,其因数个数为偶数个,第i盏灯状态与初始状态相同,为亮。所以,这个问题实际就是求解从1到m的自然数中一共有哪几个完全平方数。

这个问题怎么用c++语言来求呢?

思路如下:可以从1到m,一个一个判断i是否是完全平方数,如果是就枚举出来。判断的方法是先求i的平方根,然后取整,再把取整后的平方根进行求平方运算。如果i是完全平方数,其平方根是整数,取整后不变,对取整后的平方根进行平方运算,结果和i相等。如果i不为完全平方数,那么其平方根不为整数,取整后再进行平方运算,结果会小于i。

根据以上思路,程序代码如下:

#include <iostream>
#include <math.h>
using namespace std;
 
int main()
{
    int m;    //声明m盏灯
    cin>>m;    //键盘输入m的具体数值
    for(int i=1;i<=m;i++)    //循环求解i盏灯的状态
        {
            int squarRoot=sqrt(i);    //求i的平方根并取整
            if(squarRoot*squarRoot==i)    //如果取整后的平方根的平方与i相等,说明i的平方根为整数,i是完全平方数
            cout<<i<<endl;
        } 
    return 0;
}

运行的结果和编程方法一的结果是一样的。

3、编程方法三:求m中最大平方根法

通过观察发现,自然数m以内的完全平方数,虽然不是连续的自然数,但这些完全平方数的平方根是连续的自然数,如下表所示。

完全平方数 1 4 9 16 25 36 49 64 81 100 ...
对应平方根 1 2 3 4 5 6 7 8 9 10 ...

所以,当我们知道m的具体值时,m的平方根的整数部分为m_{0},那么m_{0}就是m以内最大完全平方数对应的那个最大平方根,{1,2,...,m_{0}}就是所有的完全平方数对应的平方根,知道了这些平方根,那么再反过来求出完全平方数的集合为{1^{2},2^{2},3^{2},...,m_{0}^{2}}。

根据以上思路的编程代码如下:

#include <iostream>
#include <math.h>
using namespace std;
 
int main()
{
    int m;    //声明m盏灯
    cin>>m;    //键盘输入m的具体数值
    int MaxsquarRoot=sqrt(m);       //求平方数为m的最大整数平方根,那么小于等于这个最大整数平方根的所有自然数的平方都是小于m的完全平方数。
    for(int i=1;i<=MaxsquarRoot;i++)    //循环求解i盏灯的状态
        {
            cout<<i*i<<endl;
        } 
    return 0;
}

对比三种编程方法,从时间复杂度上来说,方法一的时间复杂度为o(n^2),方法二为o(n),而方法三为o(1),方法三完胜!方法三不但时间复杂度低,而且适合人工求解,真是个好方法。

(全文结束)

相关推荐

  1. DAY6 作业 串口控制三亮灭

    2024-07-18 18:58:02       38 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 18:58:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 18:58:02       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 18:58:02       58 阅读
  4. Python语言-面向对象

    2024-07-18 18:58:02       69 阅读

热门阅读

  1. QT老版本下载指南

    2024-07-18 18:58:02       21 阅读
  2. react native 截图并保存到相册

    2024-07-18 18:58:02       20 阅读
  3. MySQL入门学习-深入索引.函数和表达式索引

    2024-07-18 18:58:02       24 阅读
  4. 超撒加密2.0

    2024-07-18 18:58:02       26 阅读
  5. P1009 [NOIP1998 普及组] 阶乘之和

    2024-07-18 18:58:02       22 阅读
  6. Springboot加载机制

    2024-07-18 18:58:02       17 阅读
  7. 100亿美元,得物估值到顶了吗?

    2024-07-18 18:58:02       21 阅读
  8. 3 WebAPI

    3 WebAPI

    2024-07-18 18:58:02      20 阅读
  9. Python--文件读写

    2024-07-18 18:58:02       22 阅读
  10. 【人工智能】生成式AI的未来发展方向探讨

    2024-07-18 18:58:02       21 阅读
  11. C语言 goto语句

    2024-07-18 18:58:02       19 阅读
  12. llama-cpp-python

    2024-07-18 18:58:02       21 阅读