剑指 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

方法: 动态规划

定义状态方程: d p [ i ] dp[i] dp[i]表示,有 i i i个元素时,最后剩下人的索引。(状态定义为关键点)

  1. 首先假设有五个元素时已知最后留下的索引为3,如下图:
    在这里插入图片描述

此时最后留下的元素索引为3

  1. 接下来,从其中第一次拿掉第3个数:(此时需要注意,拿掉一个元素后是需要从拿掉的那个元素下一个开始重新计数的,所以这时红色星星排第一了)
    在这里插入图片描述

此时最后留下的元素索引为0

  1. 再拿掉第3个,之后从拿掉的下一个开始重新计数:
    在这里插入图片描述

此时最后留下的元素索引为1

  1. 再拿掉第3个,从拿掉的下一个开始重新计数:

此时最后留下的元素索引为1

  1. 再拿掉第3个,从拿掉的下一个开始重新计数:
    在这里插入图片描述

此时只有一个元素了,最后留下的元素索引为1

可得出:dp[1] = 1,dp[2] = 1,dp[3] = 1,dp[4] = 0,dp[5] = 3

那么是如何能从dp[i-1]得出dp[i]呢?
我们看dp[3]和dp[4],如下图:
在这里插入图片描述
由图可以看出,将下面的3个元素最后添加一个蓝色元素,在将他们依次向右移3位,就与上面的完全相同,因此可得出,红色五星序号的变换规律, dp[i] = (dp[i-1] + m) % i ,即状态转移方程。
因为是先添加元素,再右移,所以是对添加后的元素个数求余。

状态转移方程: dp[i] = (dp[i-1] + m) % i

初始状态: d p [ 0 ] dp[0] dp[0]无意义, d p [ 1 ] = 0 dp[1] = 0 dp[1]=0

返回值: d p [ ] dp[] dp[]最后一项

python3

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        dp = [0]*(n+1) # 因为索引是从0开始的,但是存在意义的只有1个,2个...n个,所以要留n+1个空
        dp[1] = 0
        for i in range(2,n+1):
            dp[i] = (dp[i-1]+m) % i   
        return dp[-1]

C++

class Solution {
public:
    int lastRemaining(int n, int m) {
        int dp[n+1];
        dp[1] = 0;
        for(int i=2; i<=n; i++){
            dp[i] = (dp[i-1] + m) %i;

        }
    return dp[n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值