問題描述:n個人(編號0~(n-1)),從0開始報數,報到(m-1)的退出,剩下的人繼續從0開始報數。求勝利者的編號。
一般我們采用一個循環隊列來模擬約瑟夫環的求解過程,但是如果n比較大的時候,采用模擬的方式求解,需要大量的時間來模擬退出的過程,而且由於需要占用大量的內存空間來模擬隊列中的n個人,並不是一個很好的解法。
在大部分情況下,我們僅僅需要知道最后那個人的編號,而不是要來模擬一個這樣的過程,在這種情況下,可以考慮是否存在着一種數學公式能夠直接求出最后那個人的編號。
我們知道第一個人(編號一定是m%n-1) 出列之后,剩下的n-1個人組成了一個新的約瑟夫環(以編號為k=m%n的人開始):
我們先看第一個人出列后的情況,顯而易見,第一個出列的人的編號一定是m%n-1,這個人出列后,剩下的n-1個人組成了一個新的約瑟夫環,這個約瑟夫環的第一個人在最開始的環中的編號是k=m%n(就是第一個出列的人的下一個)
k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2並且從k開始報0。
事實上,可以把這個環又映射成為一個新的環:
k — 0
k+1 — 1
k+2 — 2
… ….
k-2 — n-1
可以看出,這就是原問題中把n替換成n-1的情況,假設我們已經求出來在這種情況下(即n-1個數字時)最后勝利的那個人的編號是n-1中的x,那個倒推回去的n個數字時那個人的編號就是我們要求的答案,顯而易見,這個編號應該是(x+k)%n,而k=m%n,所以這個編號為(x+m)%n.
那么如何知道n-1個人下面的這個x呢,yes,就是n-2個人情況下得到的x’倒推回去,那么如何知道n-2情況下的x’呢,當然是求n-3個人,這就是一個遞歸的過程
f(1) = 0(f(1)就是現在還剩下1個人,那么無論m為幾,這個人總會出列,因此f(1)=0)
f(n) = (f(n-1)+m)%n
那么我們要求f(n),就從f(1)倒推回去即可。
#include <stdio.h>
int main()
{
int n, m, i, s = 0;
printf (“N M = “);
scanf(“%d%d”, &n, &m);
for (i = 2; i <= n; i++)
{
s = (s + m) % i;
}
printf (“\nThe winner is %d\n”, s+1);
}
轉載請注明:約瑟夫環問題,一道經典的數據結構題目