2022-1-2 力扣每日一题 390. 消除游戏

390. 消除游戏

class Solution {
    //网上借鉴,作者思路真的太好了
    /*
    1.首先这个题目不可能用模拟的方法计算。因为最大的示例超过100000000,需要进行99999999次删减,时间上绝对来不及。

2.遇到这种时间明显不够的题目,第一想法当然是用动态规划来做。

2.1 先从最简单的情况入手:
n=1时,答案为1。
n=2时,答案为2。
。。。。。。

2.2 可以发现,答案一定不会是奇数,因为第一轮操作一定会将所有的奇数删除。这就提示出一个规律:
如果n为奇数,那么以n结尾和以n-1结尾是完全一样的。
例如1,2,3,4,5,6,7,8,9,操作一轮后剩2,4,6,8,下一轮从8开始;
而1,2,3,4,5,6,7,8,操作一轮后也剩2,4,6,8,下一轮也从8开始。
从而得到第一个递推公式:当n为奇数时,dp[n] = dp[n-1];

2.3 接下来就只剩n为偶数的情况了。
仍以1,2,3,4,5,6,7,8为例,操作一轮后剩2,4,6,8, 下一轮从8开始。
那么2,4,6,8从8开始,和2,4,6,8从2开始有什么区别呢?
很明显就是轴对称的关系,前者剩6,后者就剩(8+2 - 6);

那么2,4,6,8从2开始,和1,2,3,4从1开始有什么区别呢?
这个关系更明显,就是2倍的关系。

写到这里,大家应该明白了,1,2,3,4从1开始不就是dp[4]吗!
也就是说,dp[8] = 2*(1+4-dp[4]);  //这里的1+4-dp[4]起的就是轴对称的作用。
推而广之,n为偶数时,dp[n] = 2*(1+n/2-dp[n/2])。

这样完整的递推公式就完成了:
n为奇数时,dp[n] = dp[n-1];
n为偶数时,dp[n] = 2(1+n/2-dp[n/2])。

3.本来以为这里就做完了,然而发现了一个很尴尬的事情,n=100000000时,dp数组爆内存了。不过递推公式都有了,按照相同思路写个递归程序也不在话下了:

作者:lch-22
链接:https://leetcode-cn.com/problems/elimination-game/solution/cdong-tai-gui-hua-yu-di-gui-si-lu-by-lch-22/
*/
    public int lastRemaining(int n) {
        return cal(n);
    }
    public int cal(int n){
        if(n==1) return 1;
        if(n==2) return 2;
        if(n%2==0){
           //return cal(n)=2*(1+n/2-cal(n/2));
           return 2*(1+n/2-cal(n/2));
        }else{
            return cal(n-1);
           // return cal(n)=cal(n-1);
        }
    }
}

你可能感兴趣的:(leetcode,算法,递归法)