百人開燈問題解法及優化

yoylee_web發表於2018-10-07

一:題意

    房間裡有100盞電燈,編號為1,2,3……100,每盞燈上有一個按鈕,初始時燈全都是關的。編好號的100位同學由房間外依次走進去,將自己編號的倍數的燈的按鈕全部按一次

  • 第一位同學把編號是1的倍數的燈的按鈕按一下(此時100盞燈全亮)
  • 第二位同學把編號是2的倍數的燈的按鈕按一下(此時只有50盞燈亮著,50盞被這個人按滅了
  • ……
  • 第100位同學把編號是100的倍數的燈(即編號為100的燈)的按鈕按一下

請問依次走完後,還有多少盞燈亮著? 

二:實現

    1:暴力法

  • 雙迴圈暴力破解

  • 時間複雜度:O(n*n)

  • 空間複雜度:O(1)

  • 程式碼實現:

/**
 * 暴力法
 * 時間複雜度:O(n*n)
 */
public class Test {

       public static  void main(String[] args) {
              int[] light = new int[101];
              //初始化為燈關閉
              for (int i = 1; i < light.length; i++) {
                     light[i] = 0;
              } 
     
              //關燈操作
              for (int i = 1; i < light.length; i++) {
                     for (int j = 1; j < light.length; j++) {
                           if(j%i == 0) {
                                  light[j] = light[j] == 0 ? 1 : 0;
                           }
                     }
              }

              //輸出開啟的燈
              for (int i = 1; i < light.length; i++) {
                     if(light[i] == 1)
                           System.out.println(i);
              }

              /**
               * 輸出
               * 1
               * 4
               * 9
               * 16
               * 25
               * 36
               * 49
               * 64
               * 81
               * 100
               */
       }
}

    2:優化一:

  • 觀察發現每一盞燈都只會被比自身小並且是自身約數的值和自身 進行開啟或者關閉操作

  • 我們將自身數對燈的開啟關閉排除,則比這盞燈小並且是這盞燈約數的值為偶數,則最後為開燈,否則最後為熄燈

  • 時間複雜度會比單純的暴力法降低一些

  • 程式碼如下:

/**
 * 優化暴力法
 * 時間複雜度:O(n*n),但會比單純的暴力法時間複雜度低
 */
public class Test {

       public static  void main(String[] args) {
              int[] light = new int[101];

              //初始化為燈關閉
              for (int i = 1; i < light.length; i++) {
                     light[i] = 0;
              } 
     
              //關燈操作
              for (int i = 1; i < light.length; i++) {
                     for (int j = 1; j < i; j++) {
                           if(i%j == 0) {
                                  light[i]++;
                           }
                     }
              }

              //輸出開啟的燈
              for (int i = 1; i < light.length; i++) {
                     if(light[i]%2 == 0)
                           System.out.println(i);
              }

              /**
               * 輸出
               * 1
               * 4
               * 9
               * 16
               * 25
               * 36
               * 49
               * 64
               * 81
               * 100
               */
       }
}

    3:優化二:

  • 觀察發現,每盞燈都會被“1”操作一次,也會被自身的值操作一次,這兩次相互抵消

  • 對於燈i,如果存在一個i的約數j,那麼一定存在另外一個約數k,如果j != k ,那麼這兩次相互抵消,如果j == k,那麼只有一個這樣的值,不會被抵消,這樣只有存在完全約數(比如:4 = 2*2 , 9 = 3*3類的)的燈才不會被抵消,所以我們要求的就是具有完全約數的值。

  • 所以我們只要判斷值的開方是不是整數就可以了

  • 時間複雜度:O(n)

  • 沒有通用性,程式碼如下:

/**
 * 具體問題具體分析,沒有通用性
 * 時間複雜度:O(n)
 */

public class Test {
       public static  void main(String[] args) {

              int[] light = new int[101];
              //初始化為燈關閉
              for (int i = 1; i < light.length; i++) {
                     light[i] = 0;
              }      

              //關燈操作並輸出
              for (int i = 1; i < light.length; i++) {
                     double k =  Math.sqrt(i);
                     int m = (int)k;
                     if((double)m == k) {
                           System.out.println(i);
                     }
              }

              /**
               * 輸出
               * 1
               * 4
               * 9
               * 16
               * 25
               * 36
               * 49
               * 64
               * 81
               * 100
              */
       }
}

 

相關文章