zoukankan      html  css  js  c++  java
  • 剑指 Offer 62. 圆圈中最后剩下的数字 (约瑟夫问题)

    题目

    0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

    例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

    示例 1:

    输入: n = 5, m = 3
    输出: 3
    

    示例 2:

    输入: n = 10, m = 17
    输出: 2
    

    限制:
    1 <= n <= 10^5
    1 <= m <= 10^6

    解题思路

    1. 使用列表模拟叫号、删除数字的过程
      如n=5, m=3
      下表下划线元素,为每次删除的元素,一次删除一个。
    原始数据 0 1 2 3 4
    第1次删除 0 1 2 3 4
    剩余元素位置左移 0 1 3 4
    第2次删除 0 1 3 4
    剩余元素位置左移 1 3 4
    第3次删除 1 3 4
    剩余元素位置左移 1 3
    第4次删除 1 3
    剩余元素位置左移 3

    注意到第i次删除操作,删除元素位置都是 (i + m -1 ) % n, n是当前元素个数(表长);
    如果使用java编程,可以使用ArrayList自动维护 “剩余元素位置左移” 的操作。

    伪码

    lastRemaining(N, m)
          // 新建list 
          let list[0..n-1] = 0,1,2,...,n-1
          i = 0
          while list.size > 1 
                i = (i + m - 1) % list.size
                list.remove( i )
    return list[0]
    
    1. 考虑第k次删除,和第k-1次删除之间的数学关系
    位置 0 1 2 ... m-2 m-1 m m+1 ... n-1
    第k-1次操作后 a0 a1 a2 ... a(m-2) a(m-1) a(m) a(m+1) ... a(n-1)
    第k次删除 a0 a1 a2 ... a(m-2) a(m-1) a(m) a(m+1) ... a(n-1)
    左移剩余元素 a(m) a(m+1) ... a(n-1) a0 a1 a2 ... a(m-2)

    第k删除的元素的位置为 (m-1) % n, n = N-k,其中,N是元素初始个数,n是当前剩余元素个数(第k-1次操作后)。
    相当于在第k-1次删除后的基础上,a[m..n-1]元素都往左移m个位置,a[0..m-2]右移 n-1 - m + 1 = n - m个元素。
     
    记f(i, k),表示初始位置为i的元素,经历k次操作后的位置(前提是前k-1次,该元素都未被删除),有

    [f(i,k)= egin{cases} f(i, k-1) - m& ext{f(i, k-1) > m-1}\ f(i, k-1) + n - m& ext{f(i, k-1) < m-1} end{cases}]

    上式可以综合到一起,

    [f(i,k)= [f(i, k-1) - m] \% n], 其中, f(i, k-1) ≠ m-1,n = N - (k-1) ]

    已知什么,要求什么?
    已知的是第k次删除元素后,只剩1个元素位置为0,即为所求元素。
    也就是说,已知f(k=N-1) = 0,要求f(0),而a[f(0)]即为所求元素值。把f(k-1)写成f(k)的表达式。
    推导过程,

    [∵f(k) = [f(k-1)-m] \% n, n = N - (k-1)\ ∴f(k-1) - m = xn + f(k), x∈Z\ ∴f(k-1) = xn + f(k) + m, 式子两边对n求余, 有\ f(k-1) \% n = [f(k) + m] \% n, 而f(k-1)就是元素在长度为n(n=N-(k-1))时的位置,\ 也就是说, f(k-1)∈[0, n) \ 故f(k-1) = f(k-1) \% n = [f(k) + m] \% n \ ]

    问题转化为

    [f(k=N-1) = 0 \ f(k-1) = [f(k) + m] \% 2 \ f(k-2) = [f(k-1) + m] \% 3 \ ...\ f(1) = [f(0) + m] \% (N-1) \ f(0) = [f(1) + m] \% (N) ]

    这样,就可以利用程序进行循环/递归求解了。
    伪代码

    lastRemaining(N, m)
          k = N - 1
          pos = 0
          while k > 0
                pos= (pos + m) % (N - k + 1)
                k = k-1
          return pos
    

    参考

    剑指 Offer 62. 圆圈中最后剩下的数字

  • 相关阅读:
    第一次作业-编译原理概述
    node 升级版本,指定的版本
    v-show,v-if切换组件echarts显示不全的问题
    js点击特效动画,小人动画
    路由懒加载
    echarts tree 点击动态添加子集实例
    echarts中数据过多加入滚动条,相关属性dataZoom介绍
    解决echarts x轴标签文字过多导致显示不全
    element-ui 单选框点击整个行为选中状态
    Eclipse上Maven环境配置使用
  • 原文地址:https://www.cnblogs.com/fortunely/p/14227676.html
Copyright © 2011-2022 走看看