1. 程式人生 > >約瑟夫環問題:圓桌報數問題

約瑟夫環問題:圓桌報數問題

約瑟夫環問題:一圈共有N個人,開始報數,報到M的人自殺,然後重新開始報數,問最後自殺的人是誰?


如圖:內環表示人排列的環,外環表示自殺順序;上面N=41,M=3。

最普通辦法就是模擬整個過程:建一個bool陣列,true表示此人還活著,false表示已經自殺。可以模擬整個過程

  1. #include<iostream>
  2. usingnamespace std;  
  3. int main()  
  4. {  
  5.     int N;//人的總個數
  6.     int M;//間隔多少個人
  7.     cin>>N;  
  8.     cin>>M;  
  9.     bool *p=new
    bool[N+1];//[1……N]為true表示此人還活著
  10.     for (int i=1; i <= N; i++)  
  11.         *(p+i)=true;  
  12.     int count=0;//統計自殺的人數
  13.     for (int i=1, j=0; ;i++)//i用來表示迴圈,j用來計算是不是第N個人
  14.     {  
  15.         if (*(p+i))//此人還活著
  16.         {  
  17.             j++;  
  18.             if (j == M)  
  19.             {  
  20.                 *(p+i)=false;  
  21.                 j=0;  
  22.                 count++;//統計自殺的人
  23.             }  
  24.             if (count == N)  
  25.             {  
  26.                 cout<<"最後自殺的人是:"<<i<<endl;  
  27.                 break;  
  28.             }  
  29.         }  
  30.         if(i == N)  
  31.             i=0;  
  32.     }  
  33.     delete []p;  
  34.     return 0;  
  35. }  
模擬整個過程,複雜度為O(NM)。可以用數學方法來求解:

把問題重新描述一下:N個人(編號0~(N-1)),從0開始報數,報到(M-1)的自殺,剩下的人繼續從0開始報數。求最後自殺者的編號。

N個人編號如下:


第一個自殺的人是(M-1)%N,例如上圖中,41個人中,報到3的人自殺,則字一個自殺的人的編號是(3-1)%41=2。編號(M-1)%N自殺後,剩下的人排列如下:


有人自殺後,下一個位置M又從零開始報數,因此環應該如下:

將上面的排列順序重新編號:


問題變為(N-1)個人,報到為(M-1)的人自殺,問題規模減小了。

我們定義F(N-1)為總數為N-1個人時,最後自殺的人的序號。根據上面的對比,F(N-1)可以看成是N個人自殺掉一個之後重新編碼組成的新問題,與重新編碼之前的序號對比多了一個M,那麼F(N-1) + M就相當於是N個人自殺掉一個之後,接著繼續自殺到最後一個的序號。即F(N) = [F(N-1) + M]%N. 

這樣一直進行下去,最後剩下編號為0的人。用函式表示:

F(1)=0

當有2個人的時候(N=2),報道(M-1)的人自殺,最後自殺的人是誰?應該是在只有一個人時,報數時得到的最後自殺的序號加上M,因為報到M-1的人已經自殺,只剩下2個人,另一個自殺者就是最後自殺者,用函式表示:

F(2)=F(1)+M

可以得到遞推公式:

F(i)=F(i-1)+M

因為可能會超出總人數範圍,所以要求模

F(i)=(F(i-1)+M)%i

有了遞推公式就可以在O(N)時間求出結果:

  1. #include<iostream>
  2. usingnamespace std;  
  3. int main()  
  4. {  
  5.     int N;//人的總個數
  6.     int M;//間隔多少個人
  7.     cin>>N;  
  8.     cin>>M;  
  9.     int result=0;//N=1情況
  10.     for (int i=2; i<=N; i++)  
  11.     {  
  12.         result=(result+M)%i;  
  13.     }  
  14.     cout<<"最後自殺的人是:"<<result+1<<endl;//result要加1
  15.     return 0;  
  16. }