• 约瑟夫环之数学方法【只能求最后胜利者】+ 循环链表【实现】


    约瑟夫环描述:已知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
    请按任意键继续. . .

  • 相关阅读:
    小球(总结sort和cmp函数、结构体排序)
    垃圾装袋(标记法)【标记思想】
    种树(标记思想)【贪心算法】
    PHP 配置文件
    最大前驱路径
    PHP代码片段
    PHP 中的Trait
    BootStrapTable 错误
    工作两周总结
    工作一周总结
  • 原文地址:https://www.cnblogs.com/caixu/p/2185545.html
Copyright © 2020-2023  润新知