剑指offer 假设原始序列 L0:0,1,2,...,n-2,n-1。(C++版)

题目描述

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


思路:

假设原始序列 L0:0,1,2,...,n-2,n-1

第一趟结束:序号为 k=(m-1)%n出列
(注意这里 不是k=(m-1)%(n-1), 同样这里不一定是m-1出列,因为m-1可能大于n,例如:n=4,m=6时,查找顺序依次为0,1,2,3,0,1,正好k=(m-1)%n=(6-1)%4=1,所以,序号1出列。
所以,第一趟结束,k出列,序列变成 L1:0,1,...,k-1,k+1,...,n-2,n-1。此时,
k+1,k+2,...,n-2,n-1,0,1,...,k-1 分别对应 0,1,2,...,n-3,n-2
即:
  1. k+1 -->0
  2. k+2 -->1
  3. ...
  4. x   -->(x-k-1)%n
  5. ...
  6. k-1 -->n-2
转换方法为:观察就可以得出
右边=(左边-k-1)%n 这里注意负数取余的方法:
如-1%5=-(1%5)+5。同时,我们也可以从右边得到左边,即:
左边=(右边+k+1)%n。
结果又推出一个公式:
左边=(右边+k+1)%n=((左边-k-1)%n+k+1)%n。有哪位大佬能解释一下对吗?
接下来推导公式:假设第一趟出列前求得最终数是f(n,m),一趟出列后变成上面左边的序列,最终得数可用f '(n-1,m)表示 (因为原始序列出列一个元素后,左边序列不连续,又不是从0开始,所以用f '(n-1,m)函数),同时可得出右边序列的最终得数可用f (n-1,m)表示 (右边序列从0开始,又是连续,和原始序列一样只不过长度不同,所以可用f(n-1,m)函数表示),由题意可知,
  1. f '(n-1,m)=(f(n-1,m)+k+1)%n,    而k=(m-1)%n,带入得:
  2. f '(n-1,m)=(f(n-1,m)+m)%n,    又由题意可知:f(n,m)=f'(n-1,m),所以,
  3. f(n,m)=(f(n-1,m)+m)%n,
递推公式:
n=1时 f(n,m)=0
n>1时 f(n,m)=(f(n-1,m)+m)%n

以上都是我看其他大佬写的,自己总结出来的,有错误或者不严谨的地方欢迎指出。
上面
f'(n-1,m)=(f(n-1,m)+k+1)%n 如何推导到下面,我到现在还没有搞明白。
f'(n-1,m)=(f(n-1,m)+m)%n

代码: 这里用简单线性规划思想,利用数组来保存从0开始向后推导的结果。
class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        vector<int>a;
        if(!n||!m)return -1;
        a.push_back(0);
        for(int i=1;i<n;i++)
            a.push_back((a[i-1]+m)%(i+1));
        return a[n-1];
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_42130471/article/details/80697778
今日推荐