剑指 Offer 62. 圆圈中最后剩下的数字思路推导(约瑟夫环DP递归)
Posted --believe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer 62. 圆圈中最后剩下的数字思路推导(约瑟夫环DP递归)相关的知识,希望对你有一定的参考价值。
一、题目
剑指 Offer 62. 圆圈中最后剩下的数字(约瑟夫环、DP、递归)
题目描述
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
示例1:
输入: n = 5, m = 3
输出: 3
示例2:
输入: n = 10, m = 17
输出: 2
二、分析
这是一道典型的约瑟夫环问题。n个数字,每次删除第m个数字,求最后剩下的一个数字是多少。
我们用动态规划的思想去做这道题。首先假设 f(n,m)表示剩下的数字(可理解为最后一次删除后,剩下的数)。动态规划的思想就是要找f(n,m)和f(n-1,m)之间的关系,称为状态转换式。
先看f(n,m)中有n个数字,要每次删除m个数字。我们将其一步一步分解,看分解的子过程中有没有可以调用自身函数的小过程(递归思想)。
-
f(n,m):我们先第一次删除第m个数字即(m-1)后,下一次删除的起始元素是m(序列如下)。由于m可能大于n,所以起始元素表示为m%n。
0 1 2 3 4 5 … m-2 m-1(删除) m(下一次起点) m+1 … n-2 n-1
-
之后在n-1个数字序列中,起始元素为m%n,再不断删除m个数字(序列如下)
m(下一次起点) m+1 … n-2 n-1 0 1 2 3 4 5 … m-2 (序列1)
发现此处有符合递归的性质。为了找出递推式,我们将找其和f(n-1,m)的关系。
-
我们先分析f(n-1,m):表示有n-1个数字,起始元素为0,再不断删除m个数字(序列如下)
0 1 2 3 4 5 … m-2 m-1(删除) m m+1 … n-2(序列2)
-
由序列1和序列2映射关系可知,序列2加上m%n就对应到序列1
f ( n − 1 , m ) + ( m % n ) = 序 列 1 的 功 能 f(n-1,m)+(m \\% n)=序列1的功能 f(n−1,m)+(m%n)=序列1的功能
要保证映射转换过程一直在[0,n-1]范围内,所以要取余n,因此状态转换表达式为
f ( n , m ) = ( f ( n − 1 , m ) + ( m % n ) ) % n = ( f ( n − 1 , m ) + m ) % n f(n,m)=(f(n-1,m)+(m \\% n) )\\% n=(f(n-1,m)+m )\\% n f(n,m)=(f(n−1,m)+(m%n))%n=(f(n−1,m)+m)%n
初始条件为:
f ( 1 , m ) = 0 f(1,m)=0 f(1,m)=0
三、代码
方法 迭代、递归
/*
* 题目:剑指 Offer 62. 圆圈中最后剩下的数字
* 描述:0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
* 实现:dp动态规划
* f(n,m) = [ (m % n) + f(n-1 , m) ] % n =( f(n-1, m) + m)%n
初始条件:f(n,1) = n-1
* 复杂度:递归:时间O(N):递归需要执行N次
* 空间 O(N):递归栈空间消耗N的辅助空间
* 迭代:时间O(N):循环消耗线性辅助空间
* 空间 O(1):常量辅助空间
*/
//递归写法
int lastRemaining(int n, int m)
if (n == 1) return 0;
else return (m+lastRemaining(n-1, m)) % n;
//迭代写法
int lastRemainingA(int n, int m)
int dp1 = 0,dp2;
for (int i = 2; i <= n; i++)
dp2 = ((dp1 + m) % i);
dp1 = dp2;
//可以将上面两句合并为:dp1=((dp1 + m) % i)
return dp1;
四、总结
DP动态规划要划分为小的子问题。递归式找到就很容易,麻烦就在找递推式。
此题中映射转换那里比较难理解。
以上是关于剑指 Offer 62. 圆圈中最后剩下的数字思路推导(约瑟夫环DP递归)的主要内容,如果未能解决你的问题,请参考以下文章