【LeetCode】390. 消除游戏 Elimination Game

【LeetCode】390. 消除游戏 Elimination Game

题目描述

题目链接
给定一个从1 到 n 排序的整数列表。
首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。
第二步,在剩下的数字中,从右到左,从倒数第一个数字开始,每隔一个数字进行删除,直到列表开头。
我们不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
返回长度为 n 的列表中,最后剩下的数字。

示例:

输入:
n = 9,
1 2 3 4 5 6 7 8 9
2 4 6 8
2 6
6

输出:
6

解题思路

此题是约瑟夫问题的变体,可以总结递推公式求解。

下面通过举例来总结此问题的规律。当n=9时,子问题分解如下:

当前列表删除方向
1 2 3 4 5 6 7 8 9从左到右删除
2 4 6 8将当前列表看成1 2 3 4,结果乘以2再返回
1 2 3 4从右到左删除,结果返回
1 3将当前列表看成2 4,结果减1再返回
2 4将当前列表看成1 2,结果乘以2再返回
1 2从左到右删除,结果返回
2剩余1个数,结果返回

当n=10时,子问题分解如下:

当前列表删除方向
1 2 3 4 5 6 7 8 9 10从左到右删除
2 4 6 8 10将当前列表看成1 2 3 4 5,结果乘以2再返回
1 2 3 4 5从右到左删除,结果返回
2 4将当前列表看成1 2,结果乘以2再返回
1 2从左到右删除,结果返回
2剩余一个数,结果返回

从上面可以看出,递归时需要判断的条件有两个。一个是删除方向,一个是列表的长度是否被2整除。

如果列表长度是奇数,并且是从左到右删除,例如是 1 2 3 4 5 6 7 8 9,那么结果和 1 2 3 4 5 6 7 8 相同。
如果列表长度是奇数,并且是从右到左删除,例如是 1 2 3 4 5 6 7 8 9,那么结果和 2 4 6 8 相同,也就是 1 2 3 4 乘以2。
如果列表长度是偶数,并且是从左到右删除,例如是 1 2 3 4 5 6 7 8,那么结果和 2 4 6 8 相同,也就是 1 2 3 4 的结果乘以2。
如果列表长度是偶数,并且是从右到左删除,例如是 1 2 3 4 5 6 7 8,那么结果是 1 3 5 7 ,也就是 2 4 6 8 的结果减1,也就是 1 2 3 4 的结果乘以2再减1。

递推公式如下(其中n代表列表长度,direc代表删除方向,0代表从左到右删除,1代表从右到左删除),并且f(1)=1:
f ( n , d i r e c ) = { 2 ∗ f ( n / 2 , 1 ) , d i r e c = 0 & & n % 2 = 0 f ( n − 1 , 0 ) , d i r e c = 0 & & n % 2 ≠ 0 2 ∗ f ( n / 2 , 0 ) − 1 , d i r e c = 1 & & n % 2 = 0 2 ∗ f ( n / 2 , 0 ) , d i r e c = 1 & & n % 2 ≠ 0 f(n, direc) = \left\{ \begin{aligned} 2*f(n/2, 1),direc=0\And\And n\%2=0 \\ f(n-1, 0),direc=0\And\And n\%2\not=0 \\ 2*f(n/2, 0)-1,direc=1\And\And n\%2=0 \\ 2*f(n/2, 0),direc=1\And\And n\%2\not=0 \\ \end{aligned} \right. f(n,direc)=2f(n/2,1),direc=0&&n%2=0f(n1,0),direc=0&&n%2=02f(n/2,0)1,direc=1&&n%2=02f(n/2,0),direc=1&&n%2=0

代码如下:

class Solution {
public:
    int lastRemainingWithDirec(int n, int direction) { // direction等于0代表从左到右,等于1代表从右到左
        if (n == 1) return 1;
        else if (direction == 0) {
            if (n % 2 == 0) return 2 * lastRemainingWithDirec(n / 2, 1);
            else return lastRemainingWithDirec(n - 1, 0);
        }
        else {
            if (n % 2 == 0) return 2 * lastRemainingWithDirec(n / 2, 0) - 1;
            else return 2 * lastRemainingWithDirec(n / 2, 0);
        }
    }
    int lastRemaining(int n) {
        return lastRemainingWithDirec(n, 0);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值