約瑟夫環:
約瑟夫環(約瑟夫問題)是一個數學的應用問題:已知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 -----> 0k+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,[編程題] 刪數
輸入描述:
每組數據爲一行一個整數n(小於等於1000),爲數組成員數,如100,則對a[999]進行計算。
輸出描述:
一行輸出最後一個被刪掉的數的原始下標位置。
輸入例子:
8
輸出例子:
<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>
題目描述
<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>