约瑟夫环问题

约瑟夫环问题是一个数学的应用问题:已知M个人(以编号1,2,3...M分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到N的那个人出列;他的下一个人又从1开始报数,数到N的那个人又出列;依此规律重复下去,直到圆桌周围的人只剩下一人。

一、问题的来历

  据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 

二、递推,还是递推
  发现出题的爱出递归的大题。动态规划也是用递归思想。
  可是,目前俺还没来得及搞懂。。。感觉脑袋不够用啊。

三、问题的延展

  考官觉得直接的约瑟夫还不够搞死人,还延展了。
  N只猴子围在一圈选大王,从1开始数,数到M的猴子退出圈外,从下一只猴子继续从1开始数,数到N的猴子再退出圈外(M>N)。如果最初编号为S的猴子最后当了大王,求最小的N是多少。
 
四、解决思路
  这里有一个“陷阱”,
N只猴子,要从0开始编号,编号到(N-1),从0开始数数,数到(M-1)的猴子退出,最后得到的结果再加1,就得到从1开始编号,从1开始数数情况下的编号。因为计算机算余数,是有0的。
  M只猴子,最后只剩一只,那么要淘汰M-1轮。 
  初始编号为:        0, 1, ..., m-1, m, ..., N-1,淘汰编号m-1的猴子,从m开始重新编号0,那么,
  第1轮后编号为:N-m, N-m+1,..., 0, 1,..., N-1-m,共N-1个,可以发现,X[0]=(X[1]+m) mod N。
递推可以得到规律: X[i]=(X[i+1]+m) mod (N-i)。
  最后一轮编号必然是X[N-1]=0。那么剩下2个的时候,其编号X(N-2) =(0+m) mod 2。
  那么用一个循环来倒推,就可以得到最后剩下的猴子的原始编号了。
 int nTotleNum=0, nExit=0, nStay=0;

    cout<<"请输入总共有多少只猴子:";
    cin>>nTotleNum;
    cout<<"请输入从1数到第几个猴子退出:";
    cin>>nExit;
    //N只猴子,从0开始编号,从0开始数数,数到(m-1)的猴子退出,最后得到的结果再加1,就得到从1开始编号,从1开始数数情况下的编号。
    int nTmpPos=0;
    for(int nPos=2;nPos<=nTotleNum;nPos++)
    {
        nTmpPos = (nTmpPos+nExit)%nPos;
    }
    nTmpPos += 1;
    cout< 

如果是指定最后留下的猴子编号,那么用从2开始遍历,可以得到数到多少退出,最后留下指定编号的猴子。
 cout<<"请输入你打算最后让第几只猴子留下:";
    cin>>nStay;
    //从数到2开始
    nExit = 1;
    nTmpPos = 0;
    do
    {
        nExit++;
        nTmpPos=0;
        for(int nPos=2; nPos<=nTotleNum; nPos++)
        {
            nTmpPos = (nTmpPos+nExit)%nPos;
        }
        nTmpPos += 1;
    }while(nTmpPos!=nStay);

    cout<<"至少要数到"< 

你可能感兴趣的:(C/CPP复习)