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;//记忆化
    }
}
相关推荐
禾高网络34 分钟前
养老系统开发:背景、架构与养老行业展望
java·大数据·人工智能·小程序·架构
小雨笙笙35 分钟前
机器学习:评估模型与选择
人工智能·算法·机器学习
高亦真39 分钟前
今天是学习嵌入式的第35天
linux·学习·算法
HugoStudio_SWAN1 小时前
洛谷 P1319 / P1320 压缩技术——同一枚硬币的正反面
c++·学习·程序人生·算法
CCYe、1 小时前
新模型上线、旧模型下线:企业AI网关如何管住模型版本
java·网络·数据库·人工智能
风筝在晴天搁浅1 小时前
解题代码清爽版
java·开发语言
NeverFear7921 小时前
【黑马点评】短信登录
java·redis·tomcat·intellij-idea
2601_962298672 小时前
正则表达式引擎 哪些
java·python·perl·pcre·正则表达式引擎
孙克旭_2 小时前
JVM 入门详解:JVM、JRE、JDK 区别,JVM 基础结构梳理
java·开发语言·jvm