• 剑指offer--孩子们的游戏(圆圈中最后剩下的数字)


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

    解法一:数组(python实现)

    维持一个数组,和一个索引cur指向要删除的位置,当cur的指等于数组的大小时,令cur回到头位置

    class Solution:
        def LastRemaining_Solution(self, n, m):
            if n<1 or m < 1: return -1
            children = list(range(n))
            cur = -1
            while len(children) > 1:
                for i in range(m):
                    cur += 1
                    if(cur == len(children)):
                        cur = 0
                del children[cur]
           //在新的list中cur指向了下一个元素,为了保证移动m个的准确性,cur需要向前移动一位 cur
    -= 1 return children[0]

    解法二:循环链表(c++实现)

    构建一个循环链表依次删除喊m-1的节点,当链表中只剩一个节点时,输出这个节点的值

    class Solution {
    public:
        int LastRemaining_Solution(int n, int m)
        {
            if(n<1||m<1) return -1;
            ListNode *pHead = createCircularListNode(n);
            ListNode *pNode = pHead;
          
            while(pNode!=pNode->next){
           //将喊m-1的孩子移出圆圈
    for(int j = 1; j<m-1; j++){ pNode = pNode->next; } ListNode *ptemp = pNode->next; pNode->next = ptemp->next; free(ptemp); pNode = pNode->next; } return pNode->val; }
       // 创建循环链表 ListNode
    *createCircularListNode(int n){ ListNode *phead, *pNode; phead = NULL;
         //创建链表
    if(n!=0){ for(int i = 0; i < n; i++){ ListNode* ptemp = (ListNode*)malloc(sizeof(ListNode)); ptemp->val = i; if(phead == NULL){ phead = ptemp; pNode = phead; } else{ pNode->next = ptemp; pNode = ptemp; } }
           //将尾指针指向头指针 pNode
    ->next = phead; } return phead; } };

    时间复杂度O(n) ,空间复杂度O(n)

    解法三:数学公式(JAVA实现)

    对于n个孩子参与的游戏,第一轮游戏删除带有下划线的那个(喊m-1的孩子)  0,  1,  2,  .... m-1,  m,  m+1,  m+2, ....   n-2,   n-1

    得到新的数组  0,  1,  2,  .... m-2,  m,  m+1,  m+2, ....   n-2,   n-1

    从m处重新开始从0报数,上述数组重新排列 m,  m+1,  m+2, ....   n-2,   n-1,0,  1,  2,  ...., m-3,  m-2. 

    按照上边的顺序从0开始编号,对应的编号为 0,  1,  2,... n-(m+2),  n-(m+1),  n-m,  m-(m-1), n-(m-2), ..., n-3,  n-2

    若上一行为x' 下一行为x,则对应关系为:x= (x+m) % n

    通过上表可得,将1人出队后的数据重新组织成0-(n-2) 共计n-1个人的列表,并求由n-1个人参与,并将其中报m-1的人出列的问题.也就是说要求原问题n个人参与的解,可以先求n-1个人参与的解,然后通过转换公式得出n个人参与的解.

    因此当n=1时是规模最小的情况,F(1) = 0, 当n=2时根据公式可知问题的解F(2) = (F(1)+m)%2, 当有n个人时问题的解 F(n) = (F(n-1)+m)%n, 可以用递归也可以用递推的方式解决这个问题,如果用递归解决的话存在大量重复计算的问题,因此我们可以用递推的方式求解

    public class Solution {
        public int LastRemaining_Solution(int n, int m) {
            if(n<1||m<1) return -1;
            int cur=0;
            for(int i = 1; i < n; i++){
                cur = (cur+m)%(i+1);
            }
            return cur;
        }
    }

    时间复杂度O(n) ,空间复杂度O(1)

  • 相关阅读:
    算法复习———dijkstra求次短路(poj3255)
    NOIP2017年11月9日赛前模拟
    NOIP2017赛前模拟11月6日—7日总结
    刷题总结——子串(NOIP2015)
    NOIP2017赛前考试注意事项总结
    NOIP2017赛前模拟11月4日总结:
    NOIP2017赛前模拟11月2日总结
    刷题总结——Aeroplane chess(hdu4405)
    刷题总结——Interval query(hdu4343倍增+贪心)
    刷题总结——Throw nails(hdu4393)
  • 原文地址:https://www.cnblogs.com/laozhanghahaha/p/12249093.html
Copyright © 2020-2023  润新知