约瑟夫问题新解法

前言

        又碰到了约瑟夫问题,这样的题目本来用环形链表模拟的话就能做出来。然而,最近新学习了一种做法,实在是有点震惊到我了。无论是思路上,还是代码量上,都是那么的精彩。就想也震惊一下其他人。谁能想到原来模拟出来四五十行代码,如今只需要三行就搞定了呢?

一. 题目

        题目和约瑟夫问题的意思相同,读者可酌情跳过。

描述

    每年六一儿童节,牛客都会准备一些小礼物和小游戏去看望孤儿院的孩子们。其中,有个游戏是这样的:首先,让 n 个小朋友们围成一个大圈,小朋友们的编号是0~n-1。然后,随机指定一个数 m ,让编号为0的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0... m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客礼品,请你试着想下,哪个小朋友会得到这份礼品呢?

数据范围:1≤𝑛≤50001≤n≤5000,1≤𝑚≤100001≤m≤10000

要求:空间复杂度 𝑂(1)O(1),时间复杂度 𝑂(𝑛)O(n)

二. 解题思路

1. 传统解法

        传统接法就是环形链表模拟,先将链表链接好,然后再遍历链表到了 m 次就删除数据。循环直到剩下一个节点。

图1-1

        如此得到最后剩下的人是3号。

2. 递推解法

        这个解法就比较有意思了,重点讲讲。

       首先我们从最开始 n 个人的时候开始模拟,n 个人里面第 (m - 1)% n 号会出局,剩下的人从原来第 m 个开始从 0 编号。

图2-1

        假设在 n 个人,报数为 m 的时候,最后留下的人用 dp[n] 表示。n - 1 个人的时候, 最后留下的人用 dp[n - 1] 表示。

        那么,dp[n]dp[n - 1] 的关系是什么呢?

        dp[n] = dp[n - 1] + m

        实际上由于 dp[n] 的大小不能超过 n - 1。如图2-1左侧,所以最后结果需要取模。

        dp[n] = (dp[n - 1] + m)% n 

图2-2

        依次类推从只有一个人的时候开始向人多了推算,只有一个人的时候恒成立 dp[1] = 0 .

        根据公式 dp[2] = (dp[1] + m)% 2 ,依次向下计算即可。

        然后回到传统解法的例子,验证:

图1-1

        dp[1] = 0

        dp[2] = (dp[1] + 3)% 2 = 1 ,

        dp[3] = (dp[2] + 3)% 3 = 1,

        dp[4] = (dp[3] + 3)% 4 = 0,

        dp[5] = (dp[4] + 3)% 5 = 3,

        结果竟然和传统的模拟解法结果一样,太神奇了。

三. 解题代码

int LastRemaining_Solution(int n, int m ) {
    int f = 0;
    for(int i = 2; i <= n; ++i)
    {
        f = (f + m) % i;
    }
    return f;
}

        就这么多,核心代码就三行,不要太爽。

作者结语

        无敌的算法是真的能出奇迹,数学无敌。

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值