圆圈中最后剩下的数字
题目:0,1,…,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
输入:5,3
输出:3
两个解法:第一个、是经典的解法,用环形链表模拟圆圈
第二个、分析每次被删除的数字的规律,利用动态规划解决问题
思路:下面,我们主要介绍,用动态规划来解决问题的方式。
1、首先,我们定义一个关于n、m的方程:f(n,m),该方程表示在n个数字中每次删除第m个数字最后剩下的数字
根据1中的定义,我们可知 f(n-1,m)表示在n-1个数字中每次删除第m个数字最后剩下的数字。那么f(n,m) 与 f(n-1,m) 有什么关系呢?
我们可以想到在n个数字中删除第m个数字后,那么n个数字就变成了n-1个数字,只是这n-1个数字是从 m , m+1…n-1, 0, 1,…m-2。于是,我们就想可不可以将 f(n,m) 在删除第m个数字后,进行映射,将上述{m , m+1…n-1, 0, 1,…m-2}映射成{0, 1, 2, … n-2}
2、于是,我们构建一个映射,将{m , m+1…n-1, 0, 1,…m-2}映射成{0, 1, 2, … n-2},很容易想到p(x) = (x-m)%n,那么该映射的逆映射就是(x+m)%n
3、于是,就将 f(n,m) = [ f(n-1,m) + m ] % n (n>1),当n=1时,f(n,m)=f(n-1,m)=0,联系起来,即递归式写出来了
/*
思路:利用动态规划,找出f(n,m)与f(n,m-1)的递推关系,然后编程即可
f(n,m)的值表示n个数字中每次删除第m个数字,删除第m个数字后,从m+1开始计数,最终剩下的那个数字
*/
class Solution {
public:
/*
这是递归的做法
*/
int LastRemaining_Solution(int n, int m)
{
if(n<1){
return -1;
}
if(n==1){
return 0;
}
return (LastRemaining_Solution(n-1,m) + m ) % n;
}
/*
这是循环的做法
*/
int LastRemaining_Solution(int n, int m)
{
if(n<1){
return -1;
}
if(n==1){
return 0;
}
int remainNumber=0;
for(int i=2;i<=n;i++){
remainNumber=( remainNumber + m )%i;
}
return remainNumber;
}
};