n個人想玩殘酷的死亡遊戲,遊戲規則如下:
n個人進行編號,分別從1到n,排成乙個圈,順時針從1開始數到m,數到m的人被殺,剩下的人繼續遊戲,活到最後的乙個人是勝利者。
請輸出最後乙個人的編號。
input
輸入n和m值。m>1。
output
輸出勝利者的編號。
sample input
5 3sample output
4hint
第一輪:3被殺第二輪:1被殺第三輪:5被殺第四輪:2被殺
#include #include using namespace std; //使用命名空間std
//鍊錶法
struct lnode
;typedef struct
dlinklist;
int main()
kill.befo->pre = kill.rear;
//約瑟夫環初始化完成
int flag = 1; //計數器
lnode* t, *j; //工作指標
t = kill.befo;
while (flag < n)
cout << t->no << endl;
delete t;
} return 0;
}
(2)用陣列模擬迴圈鍊錶
#include #include using namespace std; //使用命名空間std
int main()
cout << i+1 << endl; //邏輯次序從0開始,實際編號從1開始
} return 0;
}
第乙個人(編號一定是(m-1)%n,設之為(k-1) ,讀者可以分m=n的情況分別試下,就可以得出結論) 出列之後,
剩下的n-1個人組成了乙個新的約瑟夫環(以編號為k==m%n的人開始):
k k+1 k+2 … n-2, n-1, 0, 1, 2, …,k-3, k-2
現在我們把他們的編號做一下轉換:
old -> new
k --> 0
k+1 --> 1
k+2 --> 2……
k-2 --> n-2
k-1 --> n-1
變換後就完完全全成為了(n-1)個人報數的子問題,假如我們知道這個子問題的解:例如new是最終的勝利者,那麼根據上面這個表把這個new變回去不剛好就是n個人情況的解嗎!
new ->old?(這正是從n-1時的結果反過來推n個人時的編號!)
0 -> k
1 -> k+1
2 -> k+2……
n-2 -> k-2
變回去的公式 old=(new+k)%n
那麼,如何知道(n-1)個人報數的問題的解?只要知道(n-2)個人的解就行了。(n-2)個人的解呢?只要知道(n-3)的情況就可以了 ---- 這顯然就是乙個遞迴問題:
令f[i]表示i個人玩遊戲報m退出最後勝利者的編號,最後的結果就是f[n]
遞推公式
f[1]=0;
f[i] = (f[i-1] + m) % i ; (i>1)
(1)遞迴法(反向推)
#include #include using namespace std; //使用命名空間std
//遞推法
int main()
return 0;
}int f(int n,int m)
return 0;
}
約瑟夫環問題的兩種解法(詳解)
題目 josephus有過的故事 39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也不要被敵人抓。於是決定了自殺方式,41個人排成乙個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺。然後下乙個重新報數,直到所有人都自殺身亡為止。然而josephus 和他的朋友並...
約瑟夫環的兩種實現
方法一 用乙個陣列存放每個元素,再用乙個bool陣列存放每個元素是否還在那個佇列中。每次出列乙個元素,將其對應的那個bool值置為false。迴圈輸出第m個元素直到只剩下乙個元素在佇列中。不過這個時間複雜度比較高。includeusing namespace std 時間複雜度高的方法 define...
約瑟夫環O N 解法
無論是用鍊錶實現還是用陣列實現都有乙個共同點 要模擬整個遊戲過程,不僅程式寫起來比較煩,而且時間複雜度高達o nm 當n,m非常大 例如上百萬,上千萬 的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規...