Leetcode(每日一题)——1140. 石子游戏 II

news2024/11/30 3:51:48

摘要

​​​​​​1140. 石子游戏 II

877. 石子游戏

1406. 石子游戏 III

375. 猜数字大小 II

464. 我能赢吗

486. 预测赢家

1025. 除数博弈

一、动态规划解析

Alice一开始有两个选择:拿前一堆/前两堆石子。如果 Alice 拿前一堆,那么轮到 Bob 时,他也可以在剩下的四堆石子中,拿前一堆/前两堆石子。如果 Bob 拿前一堆,那么又轮到 Alice,不断这样思考下去,可以画出如下搜索树。注意如果可以全部拿完,就全拿。

根据上图,定义dfs(i,M) 表示从]piles[i] 开始拿石子,可以得到的最大石子数。对于每个节点,由于剩余的石子总数是固定的,如果拿了某几堆石子后,对手能得到的石子数最少,那么自己能得到的石子数就是最多的。

// 尚未优化,会超时
class Solution {
    private int[] s;

    public int stoneGameII(int[] piles) {
        s = piles;
        int n = s.length;
        for (int i = n - 2; i >= 0; --i)
            s[i] += s[i + 1]; // 后缀和
        return dfs(0, 1);
    }

    private int dfs(int i, int m) {
        if (i + m * 2 >= s.length) return s[i];
        int mn = Integer.MAX_VALUE;
        for (int x = 1; x <= m * 2; ++x)
            mn = Math.min(mn, dfs(i + x, Math.max(m, x)));
        return s[i] - mn;
    }
}

由于Alice 拿一堆,Bob 拿两堆和Alice 拿两堆,Bob 拿一堆,都会递归到 dfs(3,2)。整个回溯过程是有大量重复递归调用的。由于递归函数没有副作用,无论调用dfs(i,M) 多少次,算出来的结果都是一样的,因此可以用记忆化搜索来优化:

  • 如果一个状态(递归入参)是第一次遇到,那么可以在返回前,把状态及其结果记到一个 cachecache 数组(或者哈希表)中;
  • 如果一个状态不是第一次遇到,那么直接返回 cache中保存的结果。

我要用二维数组记录的话,第二个维度开多大空间比较合适?或者说,需要被记忆化的M的上界是多少?两人交替拿石子,为了在 i 尽量小的情况下,使 M 尽量大,那么每次都拿满M堆,最后有(2+4+8+⋯+M)+2M<n。即4M−2<n,亦为 4M≤n+1,这样算的话 M 的上界为(n+1)​/4。注意后面可以根据 n 来调整,不一定要拿满2M。总而言之,需要被记忆化的 M 的上界为(n+1)​/4。

class Solution {
    private int[][] cache;
    private int[] s;

    public int stoneGameII(int[] piles) {
        s = piles;
        int n = s.length;
        for (int i = n - 2; i >= 0; --i)
            s[i] += s[i + 1]; // 后缀和

        cache = new int[n - 1][(n + 1) / 4 + 1];
        for (int i = 0; i < n - 1; i++)
            Arrays.fill(cache[i], -1); // -1 表示没有访问过
        return dfs(0, 1);
    }

    private int dfs(int i, int m) {
        if (i + m * 2 >= s.length) return s[i];
        if (cache[i][m] != -1) return cache[i][m];
        int mn = Integer.MAX_VALUE;
        for (int x = 1; x <= m * 2; ++x)
            mn = Math.min(mn, dfs(i + x, Math.max(m, x)));
        return cache[i][m] = s[i] - mn;
    }
}

复杂度分析

  • 时间复杂度:时间复杂度O(n^3)。记忆化后,每个状态只会计算一次,因此时间复杂度 == 状态个数×单个状态的计算时间。本题中状态个数等于O(n^2),而单个状态的计算时间为O(n),因此时间复杂度为O(n^3)。
  • 空间复杂度:O(n^2)。记忆化需要存储O(n^2) 个状态。

二、动态规划解析(2)

本题很明显要用记忆化搜索或者动态规划来求解,如果直接使用动态规划的话,我们要想清楚有哪些子状态需要存储。

首先一定要存储的是取到某一个位置时,已经得到的最大值或者后面能得到的最大值,但是光有位置是不够的,相同的位置有不同数量的堆可以取,所以我们还需存储当前的M值

由于本题中的状态是从后往前递推的,如:假如最后只剩一堆,一定能算出来最佳方案,但是剩很多堆时不好算(依赖后面的状态)。所以我们选择从后往前递推。

dp[i][j]表示剩余[i : len - 1]堆时,M = j的情况下,先取的人能获得的最多石子数

  1. i + 2M >= len, dp[i][M] = sum[i : len - 1], 剩下的堆数能够直接全部取走,那么最优的情况就是剩下的石子总和。
  2. i + 2M < len, dp[i][M] = max(dp[i][M], sum[i : len - 1] - dp[i + x][max(M, x)]), 其中 1 <= x <= 2M,剩下的堆数不能全部取走,那么最优情况就是让下一个人取的更少。对于我所有的x取值,下一个人从x开始取起,M变为max(M, x),所以下一个人能取dp[i + x][max(M, x)],我最多能取sum[i : len - 1] - dp[i + x],[max(M, x)]。

对于piles = [2,7,9,4,4],我们可以得到下图所示的dp数组,结果为dp[0][1]。

public int stoneGameII(int[] piles) {
    int len = piles.length, sum = 0;
    int[][] dp = new int[len][len + 1];
    for (int i = len - 1; i >= 0; i--) {
        sum += piles[i];
        for (int M = 1; M <= len; M++) {
            if (i + 2 * M >= len) {
                dp[i][M] = sum;
            } else {
                for (int x = 1; x <= 2 * M; x++) {
                    dp[i][M] = Math.max(dp[i][M], sum - dp[i + x][Math.max(M, x)]);
                }
            }
        }
    }
    return dp[0][1];
}

博文参考

《leetcode》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/363682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Propargyl-PEG1-SS-PEG1-PFP ester,1817735-30-0,炔基应用于生物标记

【中文名称】丙炔-单乙二醇-二硫键-单乙二醇-五氟苯酚酯【英文名称】 Propargyl-PEG1-SS-PEG1-PFP ester【结 构 式】【CAS号】1817735-30-0【分子式】C16H15F5O4S2【分子量】430.4【基团部分】炔基基团【纯度标准】95%【包装规格】1g&#xff0c;5g&#xff0c;10g&#xff0c…

互联网行业中,哪些岗位越老越吃香?

你是不是也想转行IT行业&#xff0c;找一门适合自己学习&#xff0c;能拿高薪的技术&#xff0c;最好还越老越吃香&#xff1f;或许先应该看看对应岗位&#xff0c;老资格的同行们可以拿到的薪资数再做判断。整体上看&#xff0c;大部分岗位的起薪水平相差不大&#xff0c;但随…

matplotlib绘制三维图

目录线状堆积图 PolygonPlot三维表面图 SurfacePlot散点图ScatterPlot柱形图 BarPlot三维直方图螺旋曲线图 LinePlotContourPlot轮廓图网状图 WireframePlot箭头图二维三维合并文本图Text三维多个子图线状堆积图 PolygonPlot Axes3D.add_collection3d(col, zs0, zdir‘z’)  …

(考研湖科大教书匠计算机网络)第六章应用层-第一、二节:应用层概述和C/S及P2P

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;应用层概述二&#xff1a;客户/服务器&#xff08;C/S&#xff09;和对等&#xff08;P2P&#xff09;方式&#xff08;1&#xff09;客户/服务器&…

Vue页面组成及常用属性

一、Vue页面组成 目前的项目中&#xff0c;Vue页面都是采用组件套娃的形式&#xff0c;由一个一个的组件拼接而成整个页面。一个组件就是一个.vue文件。组件通常由template和script两部分组成&#xff1a; template部分&#xff1a;页面展示的具体元素内容&#xff0c;比如文字…

Type-c诱骗取电芯片大全

随着Type-C的普及和推广&#xff0c;目前市面上的电子设备正在慢慢淘汰micro-USB接口&#xff0c;逐渐都更新成了Type-C接口&#xff0c;micro-USB接口从2007年上市&#xff0c;已经陪伴我们走过十多个年头&#xff0c;如今也慢慢退出舞台。 今天我们评测的产品是市面上Type-C…

【OJ】最小字典序游戏

&#x1f4da;Description: 牌王和图王在玩一个游戏。 他们需要轮流移动字符串上的L&#xff0c;R指针&#xff0c;最后一位无法移动的人会输掉游戏。 给定一个字符串 s &#xff0c;起初有两个指针 L 和 R 都指向字符串的下标为k的位置(1 < k < | s |&#xff0c;|s…

CCNP350-401学习笔记(501-550题)

501、Refer to the exhibit. What is the effect of the configuration? A. The device will allow users at 192.168.0.202 to connect to vty lines 0 through 4 using the password ciscotestkey B. The device will allow only users at 192 168.0.202 to connect to vty …

Mybatis-Plus入门系列(20) -兼容多种数据库

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录前言方案分析1. 分页2. XML自定义SQL案例演示1. 配置2. 简单分页查询3. 带方言的分页查询参考前言 在我们实际开发软件产品过程中&#xff0c;数据库的类型可能不是确定的&#xff0c;也有客户…

PHP面试题

PHP相关 php7新特性 1.类型的声明 php7可以声明函数传参的类型和返回值的类型&#xff0c;比如可以用int&#xff0c;string声明参数和返回值的类型&#xff0c;如下&#xff1a; 代码&#xff1a;declare(strict_types1); function add(int $a,int $b):int{ return $a$b;…

深入浅出C++ ——手撕AVL树

文章目录前言一、AVL 树介绍二、AVL树节点的定义三、AVL树的插入四、AVL树的旋转五、AVL树的验证六、AVL树的删除七、AVL树的性能八、AVL树的实现前言 在前面的文章中介绍了map / multimap / set / multiset 容器&#xff0c;这几个容器的底层都是按照二叉搜索树来实现的。但是…

paddlepaddle目标检测

目录 1 参考链接 2 环境 3 数据集准备 4 训练 train.py 5 导出预测模型 6 预测 源码来自作者 夜雨飘零1&#xff0c;我对参考链接的代码略有修改&#xff0c;网盘地址 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;ipl5 1 参考链接 博客地址 基…

Linux 实现鼠标侧边键实现代码与网页的前进、后退

前言 之前一直是使用windows进行开发&#xff0c;最近转到linux后使用VsCode编写代码。 但是不像在win环境下&#xff0c;使用鼠标侧边键可以实现代码的前向、后向跳转。浏览网页时也不行&#xff08;使用Alt Left可以后退&#xff09;。 修改键盘映射实在没有那么方便&…

文案女王彭芳如何转变为“百万发售系统”创始人?我们来探个究竟!

智多星老师 她的输出跟智多星老师几乎毫无二致&#xff0c;是抄袭还是纯属巧合呢&#xff1f; 你们问的这个问题我也想知道&#xff0c;为了了解真相&#xff0c;我让我的一个学生把那个叫“彭芳老师”的视频给我看&#xff0c;当看到她的简介时&#xff0c;我非常震惊&#…

启智社区“我为开源狂”第六期活动小白教程之基础活跃榜

一、写在前面 春天来啦~启智社区第六期活动也来啦&#xff01; 有奖金的哦~~ 基础活跃榜奖金根据用户活跃程度进行100-300元的激励。 挑战升级榜需要用户完成相应任务&#xff0c;达标者可获得300-1000元的激励。 邀请助力榜根据用户邀请情况进行积分累加&#xff0c;按实际达…

游戏策划想要了解编程和引擎是应该从unity入手还是ue4入手?

建议 考虑自身的职业规划考虑本公司引擎使用情况考虑自身兴趣爱好学习引擎的同时多拆解市面上主流游戏、做游戏数据及系统分析 区别 除去以上内容&#xff0c;说下unity和ue的学习及使用区别&#xff1a; 适用类型&#xff1a; 3D – 两个引擎都具有强大的3D功能&#xff0…

ctcdecode安装

一、写在前面&#xff1a;ctcdecode代码较早&#xff0c;安装过程有许多坑。本文章为ctcdecode安装成功的记录&#xff0c;可能存在不适用的情况&#xff0c;欢迎大家补充。二、致谢&#xff1a;感谢文章https://blog.csdn.net/u011550545/article/details/87926995提供的宝贵参…

HashMap(JDK1.8)源码+底层数据结构分析

HashMap 简介底层数据结构分析 JDK1.8 之前JDK1.8 之后 HashMap 源码分析 构造方法put 方法get 方法resize 方法 HashMap 常用方法测试 感谢 changfubai 对本文的改进做出的贡献&#xff01; HashMap 简介 HashMap 主要用来存放键值对&#xff0c;它基于哈希表的 Map 接口实现…

【React npm】从零搭建react脚手架,发布组件库到npm,并实现按需加载(二)

发布react组件库前情回顾介绍搭建脚手架配置babelrc配置jsconfig写入组件demo修改主入口文件配置生产环境webpack配置package.json发布实现按需加载前情回顾 前面写过一篇&#xff0c;发布单个组件到npm的&#xff1a; https://blog.csdn.net/tuzi007a/article/details/12911…

Anaconda环境配置

1.进入清华大学镜像网站Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror&#xff0c;下载稳定版Anaconda3-5.2.0&#xff0c;如下图。2.放到整理好的文件夹中&#xff0c;双击安装包进行安装。3.安装过程中需要改变的默认值如下&#xff…