算法之 数组两端取数游戏

2024-03-19 14:20

本文主要是介绍算法之 数组两端取数游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目

  同学A与B玩取数游戏。即有一个2n项的数组,其中每个都是整数且对两位同学都是可见的,两位同学轮流从 两端 取走数字(假设A同学先取)。
胜负评判:所取数之和较大者获胜(可能存在平局)。

分析

  1. 如果题目问A同学的胜负情况,那可以直接回答胜或者平局,因为数组对两位同学都是可见的,都做出最佳决策的情况下肯定是先取者获胜,或者平局。
  2. 如果题目问A同学最后会比B同学多多少分。那么可以用递归求解,我们拿一个具体的例子,{1,3,30,4}  很显然不能用贪心策略,让A同学直接取当前两端的较大值,因为那样的话,很大的30会被B取走,所以可以看出来两次取数之间有关联。我们可以看出来A同学应该取1,然后B同学取4,然后A取30,B取3,最终A同学得分为31,B为7。

    sum( i , j ) 为A同学从数组 a 下标的 i 处到 j 处这个范围下采取最佳决策后的得分与B同学的差值,所以我们最后要求的是sum( 0 , 3 ),表示A同学的最大得分
    那么思考每一步,是取左边的数还是右边的数呢?这取决于 a[左] - sum( 左+1,右)
    a[右] - sum( 左, 右-1 ) 哪个大。这里要注意不是 + 号 而是 - 号,因为A同学取完之后是B同学取。还要注意递归的终止条件。
 public static int fun(int[] a, int i, int j) {if(i +1 == j) return Math.abs(a[i]-a[j]);int temp1 = a[i] - fun(a, i+1, j);int temp2 = a[j] - fun(a, i, j-1);return Math.max(temp1,temp2);}

  鉴于以上递归过程中会大量重复计算值,可能会使递归栈的深度过大导致栈溢出,而且降低了性能,于是考虑使用Dynamic Programming动态规划来解决。
  和一般DP问题一样,有个二维数组来保存记录,DP[n][n] 和以上sum( i , j )的意义一样,n是原数组 a 的大小。
递归表达式:

DP[i][i] =a[i]
DP[i][j] = max(a[i]-DP[i+1][j], a[j]-DP[i][j-1])

写成代码的话就是:

public static int fun2(int[] nums) {int n = nums.length;int[][] dp = new int[n][n];for(int i = 0; i < n; i++)dp[i][i] = nums[i];for(int i = 0; i < n-1; i++)for(int j = i+1; j < n; j++)dp[j-i-1][j] = Math.max(nums[j-i-1] - dp[j-i][j], nums[j] - dp[j-i-1][j-1]);return dp[0][n-1];}

这里要注意的是这个矩阵的填写方向,for循环还有点难写。第一轮运算图

彩蛋

 以上方法在很久之前都见过,也没什么新意。稍加改动就可以求出A,B同学具体取的数字,但是在最近看一本《常用算法与程序设计》的时候,发现了这个题还有更简单粗暴的方式,复杂度是O(n)
 先求出序列中奇数号整数之和S1,再求出偶数号整数之和S2.
那么 | S1 - S2 | 就是A,B同学最终得分的差值了。而且,A同学不是取全体奇数号项,就是取全体偶数项,这个值得动脑筋想想。
还是例子 {1,3,30,4},那么 S1 = 1+30 = 31, S2 = 2+4 = 6
那么要胜的A同学,先取奇数项,即1,然后剩下的能取的3和4都是偶数项,B会取较大的4,然后A继续取数,那么能取的一定是奇数项了。所以证明了取的数是全体奇数项。
由此可见,什么递归,动归,还是逻辑分析动脑筋最重要啊!

这篇关于算法之 数组两端取数游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/826264

相关文章

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时