復習一下關于約瑟夫環(huán)的實現(xiàn)原理:
如果用C來寫的話,也會有許多的方法,比如1:采用鏈表(雙向鏈表)2:遞歸3:隊列(不斷出隊與入隊) 4:非遞歸循環(huán)。
現(xiàn)在簡單介紹一下非遞歸的約瑟夫算法。
首先和媳,需要明白約瑟夫環(huán)的具體規(guī)則:個人(編號0~(n-1)),從0開始報數(shù)哈街,報到(m-1)的退出留瞳,剩下的人繼續(xù)從0開始報數(shù)。求勝利者的編號骚秦。
假設從零開始報她倘,報到m-1的人退出,那么應該報m的人開始報零骤竹,再報到m-1帝牡,該報m的人報零,以此類推蒙揣,當所剩人數(shù)為二的時候靶溜,兩個人依次從0報到m-1,剩下的一個人即是最后的幸存者懒震。而在這兩個人中:
假設m=4罩息,綠色的代表剩存的人,那么在該輪報數(shù)中个扰,其所報的數(shù)則為4%2=0瓷炮;而假設m=5則有
5%2=1,在該輪中這位幸存者所報的最小數(shù)字為1递宅。
之所以按照此方法進行探究的原因為如果n=2娘香,即一共有兩個人苍狰,以m報數(shù),那么幸存者在游戲開始的編號為m%2烘绽。
依次類推淋昭,當n≠2,或者是n為更大的數(shù)時安接,這個幸存的人最開始的編號是多少m=5n=3繼續(xù)作圖
可知在共有三個人時最終幸存者的位置翔忽,本輪的開始到達結束共5人,結束后的第二個才是幸存者盏檐,故幸存者到達該輪的編號0間隔5+1個長度歇式。(5+1)%3=0,可知在該輪(n=3)中胡野,幸存者是編號0的數(shù)材失。
當n=2時,由于下一輪篩選只有一個數(shù)編號零硫豆,所以可以視為0豺憔,故(4+0)%2=0,(5+0)%3=1够庙,符合假設式子。
另抄邀,當m<n時耘眨,對式子進行檢驗,m=3境肾,n=5時剔难,有
(3+0)%4=3??? (3+1)%4=0?? 在有四個人時,綠色的應為編號3奥喻,紅色的應為編號0偶宫,符合事實,在有五個人時环鲤,(3+3)%5=1纯趋,(3+0)%5=3;綠色的為1冷离,紅色的應為3吵冒,符合事實,故解法需要從最后一個開始倒推直到人數(shù)為n時西剥,所求即為正解痹栖。
實現(xiàn)方法可以有:
s=0;a[0]=0;a[1]=0;
for(i=2;i<n;i++)
{s=(s+m)%i;a[i]=s;}
得到在一定的m下,不同總人數(shù)的一系列勝利者的原始編號瞭空。
下面來介紹題目的求解揪阿。
由于題目的要求是前k位留下疗我,后k位淘汰,所以求最終剩余的方法并不方便南捂,從后到前的時間復雜度也會變大吴裤。故使用的方法為逐個淘汰,判斷 淘汰的人是否符合要求黑毅,不符合嚼摩,則改變m。
要達到淘汰的為總人數(shù)的后半段矿瘦,所以第一次考慮m應最小為k+1枕面;
在此基礎上,for語句循環(huán)自增m缚去,判斷合理性潮秘。
合理性的判斷:
x為淘汰的人的原始編號,x=(m+s)%n????? (s初始化為0易结,表示淘汰的人后面一個人被初始化為零后枕荞,該人的前面有幾個編號的人,m+s表示的是下一次需要淘汰的人搞动,由于共有n個人躏精,故需要取模,使淘汰掉的人編號永遠大于k鹦肿,具體k個壞人的排序不需要考慮矗烛,因為是否淘汰的是壞人只需要判斷是否 <k)
如果x<k 代表淘汰掉了好人,循環(huán)退出箩溃,m++瞭吃;
如果x=0;說明淘汰掉的為最后一個人涣旨,設其編號為n歪架,目的是為了避免將x<k的判斷代入,影響結果霹陡。
如果處理后的淘汰人編號符合條件和蚪,則總數(shù)n--;
將淘汰的人的編碼記下穆律,其前面有多少人也記下惠呼。
當n--后與k相等,則說明該m符合條件峦耘,循環(huán)結束剔蹋。
#include<stdio.h>
void main()
{
int a[15];
int x, s, i, k, n, m, tem = 0;
for (i = 1;i < 15;i++)
{
tem = 0;
for (m = i + 1;;m++)
{
if (tem == 1)
break;
s = 0;n = 2 * i;
while (1)
{
x = (s + m) % n;
if (x < 1)
x = n;
if (x <= i)
break;
n--;
s = x - 1;
if (n == i)
{
a[i] = m;
tem = 1;
break;
}}}}
while (scanf("%d", &k)&&(k != 0))
printf("%d\n",a[k]);
}