蔡諝的窝

博客园 首页 新随笔 联系 订阅 管理

约瑟夫环描述:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

举例: n = 9, k = 1, m = 5

【解答】出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。

该过程可以用循环链表的方法模拟实现,不是此文的重点,不给出代码实现。

网上有人给出了约瑟夫环的数学方法,但看它的叙述让人很费解,现给出本人的描述:

撇开题目,n个人编号0,1,2,...,n-1,游戏进行一轮踢出1位同学(m-1),剩n-1人(编号0,1,2,...,m-2,m,...,n-1),对n-1人重新排队得(m,...,n-1,0,1,2,...,m-2),对这n-1人重新“标”号(0,1,2,...,n-2),好了,现在可以进行假设,现假设n-1人进行n-2次踢人后得到的最后胜利者的"标"号为x,那么请问他在上次n人游戏时的“编”号x'是多少?

这就是对应了,我们根据上段的描述可以得出:(标号+m)%n=编号,故x'=(x+m)%n

推广开了,得n人游戏的最后胜利者是f[n]=(f[n-1]+m)%n; (n>1)和f[1]=0;          

注意别忘了在得到结果后要+1还原,因为编号是从0开始,而题目描述是从1开始。            .#

现给出几点说明:

1.该数学方法没有参数k,前提是假定从0编号的人开始喊1(其实这个无关紧要,还原时+k就可以);

2.该数学方法只求出了最后胜利者,即冠军,但亚军季军还须另外想办法,所以用循环链表模拟还是具有其优势的。

3.给出n=1:9且m=5的结果做参考(别忘了+1还原哦)

  0  1  0  1  1  0  5  2  7

【参考文献】http://baike.baidu.com/view/717633.htm

-----------------------------------------------------------------------------------------

循环链表实现之——

#include <iostream>
using namespace std;
struct CLNode
{
    int data;
    CLNode *pnext;
};
CLNode* CreateCList(int n)//创建n个人的循环链表
{
    if(n<1)return NULL;
    int i=0;
    CLNode* head=new CLNode;//辅助头结点
    CLNode* ppre=head;
    CLNode* pcur=NULL;
    while (i<n)
    {
        pcur=new CLNode;
        pcur->data=++i;
        pcur->pnext=NULL;
        ppre->pnext=pcur;
        ppre=pcur;
    }
    pcur->pnext=head->pnext;
    pcur=head->pnext;
    delete head;//删除辅助头结点
    return pcur;
}
int Josephus(CLNode* head,int k,int m)//从k开始报数,报数为m的出局
{
    int winner=0;
    if(head->pnext==head){winner=head->data;delete head;return winner;}//n=1的情况
    int i=1;
    CLNode *pcur=head;
    CLNode *ppre=head;
    while(ppre->pnext!=head){//找出head的前一个结点,即尾结点,用ppre指向该结点
        ppre=ppre->pnext;
    }
    while(1){//找到第k个人,以便开始报数
        if(i==k)break;
        pcur=pcur->pnext;
        ppre=ppre->pnext;
        i++;
    }
    while(1){
        i=1;
        if(pcur->pnext==pcur){winner=pcur->data;delete pcur;return winner;}//被删除得只剩下一个结点时,就是最后胜利者
        while(1){
            if(i==m)break;//找到报m数的人,用pcur指向该结点
            pcur=pcur->pnext;
            ppre=ppre->pnext;
            i++;
        }
        ppre->pnext=pcur->pnext;
        cout<<pcur->data<<" ";//打印被删除结点的值
        delete pcur;
        pcur=ppre->pnext;
    }

}
void main()
{
    int n=1,k=1,m=5;
    while(n<=9){
        /*cout<<"n=";
        cin>>n;   
        cout<<"k=";
        cin>>k;
        cout<<"m=";
        cin>>m;*/
        cout<<"n="<<n<<"    ";
        CLNode* head=CreateCList(n);
        cout<<Josephus(head,k,m)<<endl;        //输出最后胜利者
        n++;
    }
}

输出:

n=1     1
n=2     1 2
n=3     2 3 1
n=4     1 3 4 2
n=5     5 1 3 4 2
n=6     5 4 6 2 3 1
n=7     5 3 2 4 7 1 6
n=8     5 2 8 7 1 4 6 3
n=9     5 1 7 4 3 6 9 2 8
请按任意键继续. . .

posted on 2011-09-22 21:26  蔡諝  阅读(617)  评论(0编辑  收藏  举报