A.每日一题:1140. 石子游戏 II

题目链接:1140. 石子游戏 II(中等)

算法原理:

解法:DFS

此时未做优化,会超时

时间复杂度O(N³)

①由于我们是从前往后轮流拿石子堆,到最后要是能全拿,咱就全拿,但我们不可能在全拿的时候重新遍历计算,所以此时需要用到后缀和

si:从 i 开始到末尾的后缀和(包括 pliesi)

②dfs 获取当前选手从 i 开始能拿到的最大石子数,由于 Alice 先手,所以直接返回 dfs(0,1)即可

定义:dfs(i,m,s):当前选手从 i 开始在M=m时能拿到的最大石子数

③当 i+2m≥全部堆数时,说明能直接全拿走,直接返回 si

④由于我能获取到的最大石子数=总数-对手能拿到的最小石子数,所以我们要先计算出对手能获取到的最大石子数的最小值(因为我要收益最大化)

5️⃣遍历我拿的堆数,从而计算出对手拿的堆数

为什么是Math.max(m,x)而不是x?

因为M只能变大,不能变小,在后续过程中M变大之后,再次重新循环的时候如果还是用x,那x会从1开始遍历,但实际上M此时已经很大了,不符合游戏逻辑

记忆化搜索优化

3ms击败84.35%

时间复杂度O(N³)

当 Alice 拿一堆,Bob 拿两堆 和 Alice 拿两堆,Bob 拿一堆时,都会用到 dfs(3,2),所以上述这个过程会产生大量的重复递归,所以我们可以用一个备忘录 memo 数组来存储第一次算出结果时的结果,从而避免大量的重复递归

但如果要用二维数组记录的话,第二个纬度要开多大空间比较合适??再或者说,M的最大上限是多少??(这里借助了灵神的思路~~)

两人交替拿石子,为了在 i 尽量小的情况下,使 M 尽量大,那么每次都拿满 2M 堆,最后会有:

(2+4+8+......+M)+2M<n,即 4M-2<n,亦为 4M≤n+1,算出最大上界为

Java代码:

java 复制代码
class Solution {
    //1140. 石子游戏 II
    //解法:DFS(未优化,会超时)
    public int stoneGameII(int[] piles) {
        //计算后缀和,s[i]:包括 plies[i]的后缀和
        int[] s=piles;
        int n=piles.length;
        for(int i=n-2;i>=0;i--) s[i]+=s[i+1];
        //Alice 先手,dfs结果即返回值
        return dfs(0,1,s);
    }
    //dfs(i,m,s):当前选手从 i 开始能拿到的最大石子
    private int dfs(int i,int m,int[] s){
        //能全拿走(s[i]是剩下全部石子的总和)
        if(i+m*2>=s.length) return s[i];
        //mn:对手能拿走的最大收益的最小值
        int mn=0x3f3f3f3f;
        for(int x=1;x<=m*2;x++){
            //这里的dfs从i+1开始,代表对手的最大收益
            //为啥是Math.max(m,x)而不是x?
            //因为M只能变大,不能变小,在后续过程中M变大之后,再次重新循环的时候如果还是用x,那x会从1开始遍历,但实际上M此时已经很大了,不符合游戏逻辑
            mn=Math.min(mn,dfs(i+x,Math.max(m,x),s));
        }
        //抛去对手的,就是自己的
        return s[i]-mn;
    }
}
java 复制代码
class Solution {
    //1140. 石子游戏 II
    //解法优化:记忆化搜索
    public int stoneGameII(int[] piles) {
        //计算后缀和,s[i]:包括 plies[i]的后缀和
        int[] s=piles;
        int n=piles.length;
        for(int i=n-2;i>=0;i--) s[i]+=s[i+1];
        int[][] memo=new int[n-1][(n+1)/4+1];
        for(int[] row:memo) Arrays.fill(row,-1);//-1表示没有计算过
        //Alice 先手,dfs结果即返回值
        return dfs(0,1,s,memo);
    }
    //dfs(i,m,s):当前选手从 i 开始能拿到的最大石子
    private int dfs(int i,int m,int[] s,int[][] memo){
        //能全拿走(s[i]是剩下全部石子的总和)
        if(i+m*2>=s.length) return s[i];
        //之前计算过
        if(memo[i][m]!=-1) return memo[i][m];
        //mn:对手能拿走的最大收益的最小值
        int mn=0x3f3f3f3f;
        for(int x=1;x<=m*2;x++){
            //这里的dfs从i+1开始,代表对手的最大收益
            //为啥是Math.max(m,x)而不是x?
            //因为M只能变大,不能变小,在后续过程中M变大之后,再次重新循环的时候如果还是用x,那x会从1开始遍历,但实际上M此时已经很大了,不符合游戏逻辑
            mn=Math.min(mn,dfs(i+x,Math.max(m,x),s,memo));
        }
        //抛去对手的,就是自己的
        return memo[i][m]=s[i]-mn;//记忆化
    }
}
相关推荐
小羊没烦恼!3 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
俊昭喜喜里3 天前
java中的继承和多态的区别
java
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕3 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖3 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时3 天前
01-06-A-JVM排查实战详解
java·jvm
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
vipxieliang3 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot