剑指offer系列——46.孩子们的游戏(圆圈中最后剩下的数)

Q:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
T:(约瑟夫环问题)
1.使用单向循环链表(或者使用list,vector,queue都可以,whatever,方便就好):

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};

    int LastRemaining_Solution(int n, int m) {
        if (n == 0 || m == 0)
            return -1;
        ListNode *head = new ListNode(0);
        ListNode *list = head;
        for (int i = 1; i < n; i++) {
            ListNode *temp = new ListNode(i);
            list->next = temp;
            list = list->next;
        }
        list->next = head;
        while (list->next != list) {
            for (int i = 0; i < m - 1; i++)
                list = list->next;
            ListNode *temp2 = list->next;
            list->next = temp2->next;
            temp2->next = nullptr;
            free(temp2);
        }
        return list->val;
    }

2.约瑟夫环公式
引用:https://blog.nowcoder.net/n/81a858b422804183a1a51dbfd4084ebc
这里拿a, b, c, d, e, f, g, h, i, j 来模拟10个人,设m=3.
下图的意思呢,就是模拟这10个人玩这个游戏到最后一个人存活下来的整个过程。绿色行表示在n=*时,每个人的位置索引。因为在n=10时,数一个m,杀死了c之后,后面的继续从0开始数,此时剩9个人,从0开始数,相当于这又是一个全新的游戏,只不过现在只剩9个人,而这9个人此时的位置索引也发生了变动。

通过上图,我们可以很直观的看到,最终d存活了下来。那么,此时我们从下往上看图,看看每一轮d的位置索引是什么情况。当n = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10时,d分别选择了位置索引为:0, 1, 1, 0, 3, 0, 3, 6, 0, 3。什么意思呢?可以这么理解,现在让你参与玩这个游戏,你不想死,怎么办呢,要是有1个人参与,ok,你不用死。要是总共2个人参与,你必须选择位置索引为1才能存活下来;要是总共有3个人参与,你必须选择位置索引为1才能存活;要是总共有4个人,你必须选择0......依次类推。你必须通过n来推算自己的索引位置才可以存活下来。

那么我们此时用f(n)f(n)表示nn个人的时候,能够存活的位置索引。由上图显然可以得到\(f(1)=0,f(2)=1,...,f(10)=3\)。但是我们要找的是通项公式。从图中也可以很直观的看到,n=9时d的位置索引就是n=10时循环左移了3位。那么可以得到\(f(10)=(f(9)+3)%10\),因为是循环左移,所以需要加一个取余,防止越界。同理可以得到\(f(3) =(f(2)+3)\%3,f(2) = (f(1) + 3) \% 2\)
从始至终,mm只是一个常量。所以不影响结论。所以约瑟夫环问题的公式:
\(f(n,m) = (f(n-1, m) + m) \ mod \ n\)
虽然不是在证明这个公式,但是通过这种方式,希望你能理解这个公式。

    int LastRemaining_Solution(int n, int m)
    {
        if(n == 0)
            return -1;
        if(n == 1)
            return 0;
        else
            return (LastRemaining_Solution(n - 1, m) + m) % n;
    }

猜你喜欢

转载自www.cnblogs.com/xym4869/p/12343355.html