詳解約瑟夫環(huán)問題及其相關(guān)的C語(yǔ)言算法實(shí)現(xiàn)
約瑟夫環(huán)問題
N個(gè)人圍成一圈順序編號(hào),從1號(hào)開始按1、2、3......順序報(bào)數(shù),報(bào)p者退出圈外,其余的人再?gòu)?、2、3開始報(bào)數(shù),報(bào)p的人再退出圈外,以此類推。
請(qǐng)按退出順序輸出每個(gè)退出人的原序號(hào)
算法思想
用數(shù)學(xué)歸納法遞推。
無(wú)論是用鏈表實(shí)現(xiàn)還是用數(shù)組實(shí)現(xiàn)都有一個(gè)共同點(diǎn):要模擬整個(gè)游戲過(guò)程,不僅程序?qū)懫饋?lái)比較煩,而且時(shí)間復(fù)雜度高達(dá)O(nm),若nm非常大,無(wú)法在短時(shí)間內(nèi)計(jì)算出結(jié)果。我們注意到原問題僅僅是要求出最后的勝利者的序號(hào),而不是要讀者模擬整個(gè)過(guò)程。因此如果要追求效率,就要打破常規(guī),實(shí)施一點(diǎn)數(shù)學(xué)策略。
為了討論方便,先把問題稍微改變一下,并不影響原意:
問題描述:n個(gè)人(編號(hào)0~(n-1)),從0開始報(bào)數(shù),報(bào)到(m-1)的退出,剩下的人繼續(xù)從0開始報(bào)數(shù)。求勝利者的編號(hào)。
我們知道第一個(gè)人(編號(hào)一定是m%n-1) 出列之后,剩下的n-1個(gè)人組成了一個(gè)新的約瑟夫環(huán)(以編號(hào)為k=m%n的人開始):
k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2并且從k開始報(bào)0。
現(xiàn)在我們把他們的編號(hào)做一下轉(zhuǎn)換:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
k-1 --> n-1
變換后就完完全全成為了(n-1)個(gè)人報(bào)數(shù)的子問題,假如我們知道這個(gè)子問題的解:例如x是最終的勝利者,那么根據(jù)上面這個(gè)表把這個(gè)x變回去不剛好就是n個(gè)人情況的解嗎?!!變回去的公式很簡(jiǎn)單,相信大家都可以推出來(lái):x'=(x+k)%n
如何知道(n-1)個(gè)人報(bào)數(shù)的問題的解?對(duì),只要知道(n-2)個(gè)人的解就行了。(n-2)個(gè)人的解呢?當(dāng)然是先求(n-3)的情況——這顯然就是一個(gè)倒推問題!好了,思路出來(lái)了,下面寫遞推公式:
令f[i]表示i個(gè)人玩游戲報(bào)m退出最后勝利者的編號(hào),最后的結(jié)果自然是f[n]
遞推公式
f[1]=0;
f[i]=(f[i-1]+m)%i; (i>1)
實(shí)現(xiàn)方法
一、循環(huán)鏈表
建立一個(gè)有N個(gè)元素的循環(huán)鏈表,然后從鏈表頭開始遍歷并計(jì)數(shù),如果基數(shù)i == m,則踢出該元素,繼續(xù)循環(huán),直到當(dāng)前元素與下一個(gè)元素相同時(shí)退出循環(huán)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct lnode
{
int pos;
struct lnode *next;
} lnode;
/**
* 構(gòu)建循環(huán)鏈表&&循環(huán)遍歷
*/
void create_ring(lnode **root, int loc, int n)
{
lnode *pre, *current, *new;
current = *root;
pre = NULL;
while (current != NULL) {
pre = current;
current = current->next;
}
new = (lnode *)malloc(sizeof(lnode));
new->pos = loc;
new->next = current;
if (pre == NULL) {
*root = new;
} else {
pre->next = new;
}
// 循環(huán)鏈表
if (loc == n) {
new->next = *root;
}
}
/**
* 約瑟夫環(huán)
*/
void kickoff_ring(lnode *head, int p)
{
int i;
lnode *pre, *pcur;
pre = pcur = head;
while (pcur->next != pcur) {
for (i = 1; i < p; i ++) {
pre = pcur;
pcur = pcur->next;
}
printf("%d ", pcur->pos);
pre->next = pcur->next;
free(pcur);
pcur = pre->next;
}
printf("%d\n", pcur->pos);
free(pcur);
}
void print_ring(lnode *head)
{
lnode *cur;
cur = head;
while (cur->next != head) {
printf("%d ", cur->pos);
cur = cur->next;
}
printf("%d\n", cur->pos);
}
int main()
{
int i, p, n;
lnode *head;
while (scanf("%d %d", &n, &p) != EOF) {
// 構(gòu)建循環(huán)鏈表
for (i = 1, head = NULL; i <= n; i ++)
create_ring(&head, i, n);
// 約瑟夫環(huán)
if (p != 1)
kickoff_ring(head, p);
else
print_ring(head);
}
return 0;
}
/**************************************************************
Problem: 1188
User: wangzhengyi
Language: C
Result: Accepted
Time:110 ms
Memory:912 kb
****************************************************************/
二、數(shù)組模擬
思想跟循環(huán)鏈表類似,少了構(gòu)建循環(huán)鏈表的過(guò)程
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i, index, p, n, remain, delete[3001], flag[3001] = {0};
while (scanf("%d %d", &n, &p) != EOF) {
remain = n;
index = 0;
while (remain >= 1) {
for (i = 0; i < n; i ++) {
if (flag[i] == 0) {
// 報(bào)數(shù)
index ++;
// 報(bào)p者退出圈外
if (index == p) {
// 退出圈外
flag[i] = 1;
// 重新報(bào)數(shù)
index = 0;
delete[remain - 1] = i + 1;
remain --;
}
}
}
}
// 輸出每個(gè)退出人的序號(hào)
for (i = n - 1; i >= 0; i --) {
if (i == 0) {
printf("%d\n", delete[i]);
} else {
printf("%d ", delete[i]);
}
}
}
return 0;
}
三、數(shù)學(xué)推導(dǎo)
#include <stdio.h>
int main(void)
{
int i, n, m, last;
while (scanf("%d", &n) != EOF && n != 0) {
// 接收?qǐng)?bào)數(shù)
scanf("%d", &m);
// 約瑟夫環(huán)問題
for (i = 2, last = 0; i <= n; i ++) {
last = (last + m) % i;
}
printf("%d\n", last + 1);
}
return 0;
}
上一篇:C語(yǔ)言中二維數(shù)組指針的簡(jiǎn)要說(shuō)明
欄 目:C語(yǔ)言
下一篇:詳解C語(yǔ)言的隨機(jī)數(shù)生成及其相關(guān)題目
本文標(biāo)題:詳解約瑟夫環(huán)問題及其相關(guān)的C語(yǔ)言算法實(shí)現(xiàn)
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2904.html
您可能感興趣的文章
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入理解約瑟夫環(huán)的數(shù)學(xué)優(yōu)化方法
- 01-10深入二叉樹兩個(gè)結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)- 解析最少換車次數(shù)的問題詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
- 01-10HDOJ 1443 約瑟夫環(huán)的最新應(yīng)用分析詳解
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10如何查看進(jìn)程實(shí)際的內(nèi)存占用情況詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10APUE筆記之:進(jìn)程環(huán)境詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 04-02jquery與jsp,用jquery
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實(shí)例總結(jié)


