29.剑指Offer之孩子们的游戏(圆圈中最后剩下的数)

1题目描述:

  每年六一儿童节,张东升都会准备一些小礼物去看望孤儿院的普普和严良,今年亦是如此。张朝阳作为张东升的资深摄影发烧友,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且获得张东升名贵的“爬山一日游”不归版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)。如果没有小朋友,请返回-1

2.解题思路

  f(n,m):表示每次在n个数字0,1…n-1中删除第m个数字最后剩下的数字。

  在这n个数字中,第一个被删除的数字是(m- 1)%n (取余的原因是m可能比n大),记作k,则k=(m-1)%n。删除后的序列为0,1,…k-1,k+1,…,n-1。由于下一次删除是从k+ 1开始计数的,所以相当于从标号为k+ 1,k+2, …,n-1,0,1,2…k-1的序列中继续删除第m个数字,最终剩下的数字就是结果。

  剩下的n-1个数字如果重新按顺序标号得到序列0,1,…,n-2.则每次删除第m个数,最后剩下的数字就是f(n-1,m)。由于重新标号了,所以并不是f(n, m)=f(n-1,m)。事实上,原先的不连续的序列A(k+ 1,k+2,…,n-1,0,1,2,…k-1)变成了序列B(0,1,…,n-2),而我们主要是想知道如何从序列B中的某个数找到序列A中对应的关系,先建立个映射表格:
在这里插入图片描述
  根据表格,可以很直观的看出,在B序列中数字x,对应于A序列中的(x+k+1)%n (注意:必须取余数,因为B序列中为n-k,而n- k+k+1n+1,必须取余数才能得到1)。所以在B序列中标号为f(n-1,m),对于在A序列中就为(f(n-1,m)+k+1)%n。还记得k吗?k就是在第一次删除的时候删掉的数(与n有关的变量), k=(m- 1)%n。代入上面的式子中,得到:(f(n-1,m)+k+ 1)%n = (f(n-1,m)+(m-1)+ 1)%n = (f(n-1,m)+ m)%n

  以上的推导过程记不住没关系,只要记住下面这个核心公式:
在这里插入图片描述

3.编程实现(Java):

//递归
public class LastRemaining_Solution_29 {
    public int LastRemaining_Solution(int n, int m) {
        if (n < 1 || m < 1)
            return -1;
        if (n == 1)
            return 0;
        return (LastRemaining_Solution(n - 1, m) + m) % n;
    }
}

//迭代
    public int LastRemaining_Solution(int n, int m) {
        if (n < 1 || m < 1)
            return -1;
        if (n == 1)
            return 0;
        int last = 0;
        for (int i = 2; i <= n; i++) {
            last = (last + m) % i;
        }
        return last;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值