九度OJ-题目1356:孩子们的游戏(圆圈中最后剩下的数)

题目链接地址:

九度OJ-题目1356:孩子们的游戏(圆圈中最后剩下的数)


题目描述:
每年六一儿童节,JOBDU都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为JOBDU的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为1的小朋友开始报数。每次喊到m的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续1...m报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到JOBDU名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?

输入:
输入有多组数据。
每组数据一行,包含2个整数n(0<=n<=1,000,000),m(1<=m<=1,000,000),n,m分别表示小朋友的人数(编号1....n-1,n)和HF指定的那个数m(如上文所述)。如果n=0,则结束输入。

输出:
对应每组数据,输出最后拿到大奖的小朋友编号。
样例输入:
1  10
8  5
6  6
0


样例输出:
1
3
4


解题思路:

从题目的描述可以看出这个就是传说中的约瑟夫环问题,以前在学C++的时候做过这道题,用的是循环链表。再看看这道题的时间限制居然有10S!于是没有多想,写了个循环链表,一提交居然超时了。。。 只好请教范神,大神说求约瑟夫环可以直接套用数学公式的。后来上网查了一下,还真有这个公式,具体可以参考 约瑟夫问题。

下面介绍一下算法流程:
假设初始约瑟夫环为0,1,2,...n-2,n–1,每次报到 m-1的元素被踢出环;
则第一次报数完毕后,编号为m–1的元素必定被踢出环,此时圈子里还剩下:m,m+1,m+2 ... n-2,n-1,0,1,2,... m-2
而第二次从圆圈的第m号重新开始报数,于是对这剩下的n - 1个数重新进行编号(这是解决本题最关键的一步),重新编号的映射关系如下:
m       -->    0
m+1   -->    1
m+2   -->    2
......
n-1     -->    n-m-1
0        -->    n-m
......
m-2    -->    n-2
此时问题的本质不变,但是问题的规模却从n减少到了n–1,所以我们可以利用递归的思想来解题:
假设具有n个元素的约瑟夫环,将每次报m-1的元素踢出后,最后所剩下的元素编号是X(n) (n为约瑟夫环的元素个数)。
则可以得知在原始0,1,2,...n-3,n-2,n-1约瑟夫环中最后剩下的数是X(n),
在删除m - 1后重新编号的0,1,2,...n-3,n-2约瑟夫环中,最后剩下的数是X(n-1),

由上面重新编号的映射关系可以得出:(X(n) - m) % n =X(n-1),
利用数学公式 (X(n)  - m) % n = X(n-1) 可以推算出X(n) = (X(n-1) + m) % n;
同理想要得出X(n-1),可以由(X(n-1) - m) % (n - 1) = X(n-2)推出X(n-1) = (X(n-2)  + m) % (n - 1);
……
以此类推,当n = 2时,X(2)  = (X(1)  + m) % 2;
当n = 1时,约瑟夫环中只有一个编号为0的元素,因此X(1)  = 0。
X(1)  = 0,X(n) = (X(n-1) + m) % n 这两个条件可以推算出X(n) 
需要注意的是因为题目中孩子编号从1开始,而上面算法中的元素编号从0开始,所以最后的结果X(n) 还要加上1。

AC代码如下:

#include
int main()
{
    int n,m;
    int result;               // 约瑟夫环中最后剩下的数字
    int i;
    while(EOF != scanf("%d",&n))
    {
        if(0 == n)
           break;
        scanf("%d",&m);
        result = 0;             // 当n == 1时,result = 0;
        for(i = 2;i <= n;i++)
        {
            result = (result + m) % i;  // 利用递推公式求出当问题规模为i时的解,最后得出问题规模为n的解
        }
        printf("%d\n",result + 1);      // 因为题目中的孩子编号从1开始,所以最后的结果要加1
    }
    return 0;
}
 
/**************************************************************
    Problem: 1356
    User: blueshell
    Language: C
    Result: Accepted
    Time:300 ms
    Memory:912 kb
****************************************************************/


你可能感兴趣的:(剑指Offer,面试题,九度OJ-剑指Offer)