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;//记忆化
    }
}
相关推荐
信奥卷王14 分钟前
[GESP202503 六级] 环线(含视频)
算法
CQU_JIAKE27 分钟前
10.15【A】
算法
晴天的雨.99230 分钟前
【C++】模板进阶
开发语言·c++·算法
倔强的石头10633 分钟前
【Linux指南】进程通信系列(二):匿名管道 —— pipe 的本质、fork 继承与四种读写规则
java·linux·运维
调试优选官39 分钟前
2026年IoT物联网开发赛道年度榜单深度解析:D-coding物联网系统定制与源码交付能力专项解读
java·物联网·struts·iot·开发经验
木井巳41 分钟前
【动态规划】三步问题
java·算法·leetcode·动态规划·推荐算法
小张成长计划..43 分钟前
【Linux】25:线程概念和控制
java·linux·jvm
楚识科技1 小时前
手写 OCR 识别技术深度解析:从算法原理到政企级落地实现
算法·ocr
leobertlan9 小时前
痛苦系列 | DSP-03 跨越单位圆:Z变换与零极点分析
算法·程序员
蛋蛋的就会顺顺的10 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣