約瑟夫環問題

約瑟夫環:
它是一個數學的應用問題:已知n個人(以編號1,2,3…n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。
算法原理:
約瑟夫環運作如下:
1、一羣人圍在一起坐成 [2] 環狀(如:N)
2、從某個編號開始報數(如:K)
3、數到某個數(如:M)的時候,此人出列,下一個人重新報數
4、一直循環,直到所有人出列 [3] ,約瑟夫環結束
應用場景:
猴子選大王:
由M(M <= 100)只猴子圍成一圈,從1到M進行編號,打算從中選出一個大王。經過協商,決定出選大王的規則:從第一個開始循環報數,數到N(N <= 100)的猴子出圈,最後剩下來的就是大王。問哪一個編號的猴子成爲大王。
通常解決這類問題時我們把編號從0~n-1,最後 [1] 結果+1即爲原問題的解。
首先,我們把這n個猴子的序號編號從0~n - 1(當m大於等於n時,第一個出列的猴子編號是m % n,但是m % n 可能等於0,如果從1開始,出現0這個情況,就會漏掉。這樣編號有助於簡化出列過程),當數到m - 1的那個猴子出列:
分析:
第一次出列:
所有猴子編號:0, 1, 2, 3, 4,… n - 2, n - 1
第一次出列的猴子編號爲 (m - 1)% n1, 之後它的下一個猴子又從0開始報數。
假設k1 = m % n1,(n1爲當前猴子總數),第一個猴子出列之後,k1則是下一次新的編號序列的首位元素。因此,可以得到新的序列:
k1, k1+1, k1+2, k1+3, …, n - 2, n - 1, 0, 1, 2, …, k1-3, k1-2 (k1 - 1 第一次已經出列)。我們知道,第一個猴子還是從0開始報數,可得:
0, 1, 2, 3,…, n - 2
注意:
第二次每個猴子報的相應數字與第一次時自己相應的編號對應起來。
0 ——>k1
1 ——>k1 + 1
2 ——>k1 + 2

n - 2 ——> (k1 + (n - 2)) % n1
(n1爲當前序列的總猴子數,因爲是循環的序列,k1+n-1可能大於總猴子數)。
現在我們要解決的就是n - 1階的約瑟夫問題。
後面的過程與前兩次的過程一模一樣,那麼遞歸處理下去,直到最後只剩下一個猴子的時候,便可以直接得出結果
當我們得到一個猴子的時候(即一階約瑟夫環問題)的結果。
假如得到了這個n-1階約瑟夫環問題的結果爲x(即最後一個出列的猴子編號爲x),那麼我們通過上述分析過程,可以知道,n階約瑟夫環的結果
(x + k) % n (n爲當前序列的總猴子數),而k = m%n。
那麼有:
(x + m % n) % n,那麼我們還可以將該式進行一下簡單的化簡:

當m < n時,易得上式可化簡爲:(x + m)% n

而當m >= n時,那麼上式則化簡爲:(x % n + m % n % n)% n
即爲:(x % n + m % n)% n
而 (x + m)% n = (x % n + m % n)% n
因此得證
(x+ m % n) % n = (x + m)% n
這樣的話,我們就得到了遞推公式,由於編號是從0開始的,那麼我們可以令
f[1] = 0; //當一個猴子的時候,出隊猴子編號爲0
f[n] = (f[n-1] + m)%n //m表示每次數到該數的猴子出列,n表示當前序列的總猴子數。
代碼如下:

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		int n, m, i, x = 0;
		Scanner sca = new Scanner(System.in);
		n = sca.nextInt();
		m = sca.nextInt();
		for (i = 2; i <= n; i++)
		{
			x = (x + m) % i;
		}
		System.out.println(x + 1);
	}
}

測試數據:
輸入 —— 輸出
14 3 ——> 2
11 3 ——> 7
2 3 ———> 2
2 2 ———> 1
3 3 ———> 2

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