約瑟夫環的問題與應用(JAVA)

約瑟夫環:

     約瑟夫環(約瑟夫問題)是一個數學的應用問題:已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。通常解決這類問題時我們把編號從0~n-1,最後[1]  結果+1即爲原問題的解。

  這裏可以實際爲從第1個人開始報數,報到m時停止報數,報m的人出圈,再從他的下一個人起重新報數,報到m時停止報數,報m的出圈,……,如此下去,直到所有人全部出圈爲止。當任意給定n和m後,設計算法求n個人出圈的次序。  稍微簡化一下。

        問題描述:n個人(編號0~(n-1)),從0開始報數,報到(m-1)的退出,剩下的人繼續從0開始報數。求勝利者的編號。

 

      下面利用數學推導,如果能得出一個通式,就可以利用遞歸、循環等手段解決。下面給出推導的過程:

        (1)第一個被刪除的數爲 (m - 1) % n。

        (2)假設第二輪的開始數字爲k,那麼這n - 1個數構成的約瑟夫環爲k, k + 1, k + 2, k +3, .....,k - 3, k - 2。做一個簡單的映射。

             k         ----->  0 
             k+1    ------> 1 
             k+2    ------> 2 
               ... 
               ... 

             k-2    ------>  n-2 

        這是一個n -1個人的問題,如果能從n - 1個人問題的解推出 n 個人問題的解,從而得到一個遞推公式,那麼問題就解決了。假如我們已經知道了n -1個人時,最後勝利者的編號爲x,利用映射關係逆推,就可以得出n個人時,勝利者的編號爲 (x + k) % n。其中k等於m % n。代入(x + k) % n  <=>  (x + (m % n))%n <=> (x%n + (m%n)%n)%n <=> (x%n+m%n)%n <=> (x+m)%n

        (3)第二個被刪除的數爲(m - 1) % (n - 1)。

        (4)假設第三輪的開始數字爲o,那麼這n - 2個數構成的約瑟夫環爲o, o + 1, o + 2,......o - 3, o - 2.。繼續做映射。

             o         ----->  0 
             o+1    ------> 1 
             o+2    ------> 2 
               ... 
               ... 

             o-2     ------>  n-3 

         這是一個n - 2個人的問題。假設最後的勝利者爲y,那麼n -1個人時,勝利者爲 (y + o) % (n -1 ),其中o等於m % (n -1 )。代入可得 (y+m) % (n-1)

         要得到n - 1個人問題的解,只需得到n - 2個人問題的解,倒推下去。只有一個人時,勝利者就是編號0。下面給出遞推式:

          f [1] = 0; 
          f [ i ] = ( f [i -1] + m) % i; (i>1) 

<span style="font-size:14px;">f(i)表示i個人玩遊戲最後勝利者的編號,這裏人員編號從0到i-1,報數從是從0到m-1</span>

        有了遞推公式,實現就非常簡單了,具體可以看歷年的一些真題


1,[編程題] 刪數

     有一個數組a[N]順序存放0-N,要求沒隔兩個數刪掉一個數,到末尾時循環至開頭繼續進行,求最後一個被刪掉的數的原始下標位置。以8個數(N=7)爲例:{0,1,2,3,4,5,6,7},0->1->2(刪除)->3->4->5(刪除)->6->7->0(刪除),如此循環直到最後一個數被刪除。

輸入描述:
每組數據爲一行一個整數n(小於等於1000),爲數組成員數,如100,則對a[999]進行計算。


輸出描述:
一行輸出最後一個被刪掉的數的原始下標位置。

輸入例子:
8

輸出例子:
6
分析:這題和約瑟夫環問題一致

代碼:
<span style="font-size:14px;">import java.util.*;
public class Main {
    public static void main(String[] args) {
        //約瑟夫環問題
        /**
         * 約瑟夫環的問題;
         * f(1)=0;
         * f(i)=(f(i-1)+m)%i
         * f(i)表示i個人玩遊戲最後勝利者的編號,這裏人員編號從0到i-1,報數從是從0到m-1
         */
        Scanner sc=new Scanner(System.in);
        while(sc.hasNextInt()){
            int n=sc.nextInt();
            if(n<=0)
                System.out.println(-1);
               int a[]=new int[n+1];
               a[1]=0;
               for (int i = 2; i < a.length; i++) {
                a[i]=(a[i-1]+3)%i;
            }
               System.out.println(a[n]);
        }
    }
}
</span>




還有個題目,劍指offer上面的

2,孩子們的遊戲(圓圈中最後剩下的數)

題目描述

每年六一兒童節,NowCoder都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作爲NowCoder的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到m的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到NowCoder名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試着想下,哪個小朋友會得到這份禮品呢?
<span style="font-size:14px;">public class Solution {
    public int LastRemaining_Solution(int n, int m) {
            /**
     * 約瑟夫環的問題;
     * f(1)=0;
     * f(i)=(f(i-1)+m)%i
     * f(i)表示i個人玩遊戲最後勝利者的編號,這裏人員編號從0到i-1,報數從是從0到m-1
     */
/*法一:
    if(n==0||m==0) return -1;
   int a[]=new int[n+1];
   a[1]=0;
   for (int i = 2; i < a.length; i++) {
    a[i]=(a[i-1]+m)%i;
}
   return a[n];
*/       
 if(n==0||m==0) return -1;
    int result=0,count=1;
while((++count)<=n){
    result=(result+m)%count;

}
   return result;
    }
}</span>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章