当前位置: 首页 > news >正文

暴力递归转动态规划(二)

上一篇已经简单的介绍了暴力递归如何转动态规划,如果在暴力递归的过程中发现子过程中有重复解的情况,则证明这个暴力递归可以转化成动态规划。
这篇帖子会继续暴力递归转化动态规划的练习,这道题有点难度。

题目
给定一个整型数组arr[],代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌。规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左边或者最右边的牌,玩家A和玩家B都绝顶聪明,请返回最后获胜者的分数。

暴力递归
依然是先从暴力递归开始写起,一个先手拿,一个后手拿,两个人都绝顶聪明,都知道怎么拿可以利益最大化。
先手的拿完第一个之后,再拿的时候,就要从后手拿完的数组里再挑选了。
同理,如果后手的等先手的拿了之后,是不是就可以从剩余的数组里挑选最大利益的拿了。
依然先确定base case:
如果先手拿,最理想的状态就是当数组剩下最后一个数,依然可以被我拿走。
如果后手拿,最悲催的连数组最后一个数我都拿不到。
代码中f()函数是代表在数组L~ R范围上返回上先手拿能拿到的最大值返回。
g()函数代表在数组L ~ R范围上后手拿,能够获取的最大值。
需要注意的是身份的转变,如果先手拿之后,再拿的时候就会变成后手,第二个后手拿的时候,虽然我是后手,但是也是从数组中挑选利益最大的拿,留给先手拿的人的也是不好的,所以我会变成先手。

//先手方法
public static int f(int[] arr,int Lint R){//base case:先手拿,并且数组中剩一个元素,我拿走if(L == R){return arr[L];}//因为可以选择从左边拿和右边拿,从左边拿下一次就是L + 1开始,右边拿就是 R - 1 开始。//需要注意的是我从左或者从右拿完之后,再拿就是拿别人拿剩下的了,要以后手姿态获取其余分数,所以要调用g()方法int p1 = arr[L] + g(arr,L + 1,R);int p2 = arr[R] + g(arr, L, R -1);//两种决策中取最大值return Math.max(p1,p2);
}
//后手方法
public static int g(int[] arr,int L,int R){//剩最后一个也不是我的,毛都拿不到,return 0if(L == R){return 0;}//后手方法是在先手方法后,挑选最大值,那如果先手方法选择了L,则我要从L + 1位置选,//如果先手选择了R,那我要从R - 1位置开始往下选。//是从对手选择后再次选择最大值int p1 = f(arr,L + 1,R);int p2 = f(arr,L,R - 1);//因为是后手,是在先手后做决定,是被迫的,所以取Min。return Math.min(p1,p2);
}

先手后手方法已经确定,来看主流程怎么调用

public static int win1(int[] arr){//如果是无效数组,则返回一个无效数字 -1 if(arr == null || arr.length == 0){return -1;}int first = f(arr, 0 ,arr.length - 1);int second = g(arr,0,arr.length - 1);return Math.max(first,second);
}

暴力递归的分析和代码已经搞定,接下来我们通过分析暴力递归的调用过程来实现第一步的优化,找它的依赖,找它的重复解。
举一个具体的例子,arr[]范围 0~ 7,根据上面暴力递归的代码逻辑,我们来看看它的依赖关系和调用过程。如果确定了可变参数以及依赖关系,是不是就可以尝试着优化成动态规划。
在这里插入图片描述
根据代码逻辑,要么是取左边L + 1,要么是取右边 R - 1,所以可以确定可变参数是L和R,并且整个流程下来会发现有重复解的情况。
不过有些不同的是,这个是双层递归循环依赖调用,所以如果根据可变参数参数L,R来构建缓存表的话,则需要2个不同的缓存表分别记录。

优化
前面已经分析出整个暴力递归的调用过程,并发现了重复解,其中可变参数是L、R,根据L、R构建缓存表,因为是f()和g()的循环依赖调用,所以需要准备两张缓存表。

public static int win2(int[] arr) {if (arr == null || arr.length == 0) {return -1;}int N = arr.length;int[][] fmap = new int[N][N];int[][] gmap = new int[N][N];for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {fmap[i][j] = -1;gmap[i][j] = -1;}}int first = f1(arr, 0, arr.length - 1, fmap, gmap);int second = g1(arr, 0, arr.length - 1, fmap, gmap);return Math.max(first, second);}public static int f1(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {// != -1,说明之前计算过该值,直接返回即可if (fmap[L][R] != -1) {return fmap[L][R];}int ans = 0;if (L == R){ans = arr[L];}else{int p1 = arr[L] + g1(arr, L + 1, R, fmap, gmap);int p2 = arr[R] + g1(arr, L, R - 1, fmap, gmap);ans = Math.max(p1, p2);}//这一步能够取得的最大值fmap[L][R] = ans;return ans;}public static int g1(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {if (gmap[L][R] != -1){return gmap[L][R];}//因为如果 L == R,后手方法会返回0,默认ans也是等于0,省略一步判断int ans = 0;if (L != R){int p1 = f1(arr,L + 1,R,fmap,gmap);int p2 = f1(arr,L,R - 1,fmap,gmap);ans = Math.min(p1,p2);}gmap[L][R] = ans;return ans;}

二次优化
我们上面已经创建了缓存表,并找到了变量L、R,我们现在不妨举一个例子,并将缓存表画出来,来看一下表中每一列的对应关系,如果我们能找到这个缓存表的对应关系,是不是将表构建出来以后,就可以直接获取获胜者的最大值。
在这里插入图片描述
数组arr = {7,4,16,15,1} 因为有两张缓存表,所以需要将两张表的依赖关系都找出。接下来,回到最开始的暴力递归方法,根据代码逻辑一步一步找出依赖关系。

public static int win1(int[] arr) {if (arr == null || arr.length == 0) {return -1;}int first = f(arr, 0, arr.length - 1);int second = g(arr, 0, arr.length - 1);return Math.max(first, second);}public static int f(int[] arr, int L, int R) {if (L == R) {return arr[L];}int p1 = arr[L] + g(arr, L + 1, R);int p2 = arr[R] + g(arr, L, R - 1);return Math.max(p1, p2);}public static int g(int[] arr, int L, int R) {if (L == R) {return 0;}int p1 = f(arr, L + 1, R);int p2 = f(arr, L, R - 1);return Math.min(p1, p2);}

从先手方法f()和后手方法g()的base case可以看出,如果当L == R时,f()方法中此时就是等于数组arr[L]本身的值,而g()中为0,又因为,每次我只选L或只选R,当L = R时就return了,所以我的L始终不会 > R。我们所要求的L ~ R 范围是整个数组0 ~ 4的值,此时图可以填充成这样。
在这里插入图片描述
再来接着往下看,如果此时LR随便给一个值,比如说当前fmap中L = 1,R = 3,来接着看它的依赖过程。
在这里插入图片描述
根据代码可以看出,它依赖的是g()方法中L +1和R - 1,所以对应在gmap中的依赖就是圆圈标记的部分。对应的,同样 L = 1 R = 3在gmap中也是依赖fmap对应的位置。
在这里插入图片描述
那现在有缓存表中每个位置的依赖关系,还有fmap和gmap当L == R时的值,是不是就可以推算出其他格子中的值。

代码

 public static int win3(int[] arr) {if (arr == null || arr.length == 0) {return -1;}int N = arr.length;int[][] fmap = new int[N][N];int[][] gmap = new int[N][N];//根据base  case填充fmap,gmap都是0,数组初始化值也是0,不用填充for (int i = 0; i < N; i++) {fmap[i][i] = arr[i];}//根据对角线填充,从第一列开始for (int startCol = 1; startCol < N; startCol++) {int L = 0;int R = startCol;while (R < N) {//将调用的g()和f()都替换成对应的缓存表fmap[L][R] = Math.max(arr[L] + gmap[L + 1][R], arr[R] + gmap[L][R - 1]);gmap[L][R] = Math.min(fmap[L + 1][R], fmap[L][R - 1]);L++;R++;}}//最后从L ~ R位置,取最大值return Math.max(fmap[0][N -1],gmap[0][N-1]);}

相关文章:

暴力递归转动态规划(二)

上一篇已经简单的介绍了暴力递归如何转动态规划&#xff0c;如果在暴力递归的过程中发现子过程中有重复解的情况&#xff0c;则证明这个暴力递归可以转化成动态规划。 这篇帖子会继续暴力递归转化动态规划的练习&#xff0c;这道题有点难度。 题目 给定一个整型数组arr[]&…...

debian apt error: Package ‘xxx‘ has no installation candidate

新的debian虚拟机可能会出现这个问题。 修改apt的source.list&#xff0c;位于/etc/apt/source.list&#xff0c;添加两行&#xff1a; deb http://deb.debian.org/debian bullseye main deb-src http://deb.debian.org/debian bullseye main执行&#xff1a; sudo apt-get u…...

c#设计模式-结构型模式 之 外观模式

概述 外观模式&#xff08;Facade Pattern&#xff09;又名门面模式&#xff0c;隐藏系统的复杂性&#xff0c;并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式&#xff0c;它向现有的系统添加一个接口&#xff0c;来隐藏系统的复杂性。该模式…...

Focal Loss-解决样本标签分布不平衡问题

文章目录 背景交叉熵损失函数平衡交叉熵函数 Focal Loss损失函数Focal Loss vs Balanced Cross EntropyWhy does Focal Loss work? 针对VidHOI数据集Reference 背景 Focal Loss由何凯明提出&#xff0c;最初用于图像领域解决数据不平衡造成的模型性能问题。 交叉熵损失函数 …...

运算符(个人学习笔记黑马学习)

算数运算符 加减乘除 #include <iostream> using namespace std;int main() {int a1 10;int a2 20;cout << a1 a2 << endl;cout << a1 - a2 << endl;cout << a1 * a2 << endl;cout << a1 / a2 << endl;/*double a3 …...

开源与专有软件:比较与对比

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

openResty+lua+redis实现接口访问频率限制

openResty简介&#xff1a; OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台&#xff0c;其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。 OpenResty 通过汇聚各种设…...

自动化测试(三):接口自动化pytest测试框架

文章目录 1. 接口自动化的实现2. 知识要点及实践2.1 requests.post传递的参数本质2.2 pytest单元测试框架2.2.1 pytest框架简介2.2.2 pytest装饰器2.2.3 断言、allure测试报告2.2.4 接口关联、封装改进YAML动态传参&#xff08;热加载&#xff09; 2.3 pytest接口封装&#xff…...

Python --datetime模块

目录 1&#xff0c; 获取datetime时间 2&#xff0c; datetime与timestamp转换 2-1&#xff0c; datetime转timestamp 2-2&#xff0c; timestamp转datetime 3&#xff0c; str格式与datetime转换 3-1&#xff0c; datetime转str格式 3-2&#xff0c; str格式转datetime…...

顺序表链表OJ题(3)——【数据结构】

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 前言&#xff1a; 今天是链表顺序表OJ练习题最后一次分享&#xff0c;每一次的分享题目的难度也再有所提高&#xff0c;但是我相信大家都是非常机智的&#xff0c;希望看到博主文章能学到东西的可以一键三连关注一下博主…...

【Azure】Virtual Hub vWAN

虚拟 WAN 文档 Azure 虚拟 WAN 是一个网络服务&#xff0c;其中整合了多种网络、安全和路由功能&#xff0c;提供单一操作界面。 我们主要讨论两种连接情况&#xff1a; 通过一个 vWAN 来连接不通的 vNET 和本地网络。以下是一个扩展的拓扑 结合 vhub&#xff0c;可以把两个中…...

React Navigation 使用导航

在 Web 浏览器中&#xff0c;您可以使用锚标记链接到不同的页面。当用户单击链接时&#xff0c;URL 会被推送到浏览器历史记录堆栈中。当用户按下后退按钮时&#xff0c;浏览器会从历史堆栈顶部弹出该项目&#xff0c;因此活动页面现在是以前访问过的页面。React Native 不像 W…...

双指针算法,基础算法实践,基本的算法的思想,双指针算法的实现

一&#xff0c;定义 双指针算法是一种常用于解决数组和链表问题的算法技巧。它的核心思想是使用两个指针在数据结构中按照一定的规则移动&#xff0c;从而达到快速搜索或处理数据的目的。这个技巧通常用于优化算法&#xff0c;降低时间复杂度&#xff0c;提高程序的执行效率。…...

idea http request无法识别环境变量

问题描述 创建了环境变量文件 http-client.env.json&#xff0c;然后在*.http 文件中引用环境变量&#xff0c;运行 HTTP 请求无法读取环境变量文件中定义的变量。 事故现场 IDEA 版本&#xff1a;2020.2 2021.2 解决步骤 2020.2 版本环境变量无法读取 2021.2 版本从 2020.…...

性能测试常见的测试指标

一、什么是性能测试 先看下百度百科对它的定义 性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。我们可以认为性能测试是&#xff1a;通过在测试环境下对系统或构件的性能进行探测&#xff0c;用以验证在生产环境下系统性能…...

并发 04(Callable,CountDownLatch)详细讲解

并发 Callable 1 可以返回值 2可以抛出异常 泛型指的是返回值的类型 public class Send {public static void main(String[] args) {//怎么启动Callable//new Thread().start();Aaa threadnew Aaa();FutureTask futureTasknew FutureTask(thread);new Thread(futureTask,&qu…...

Json路径表达式

原json路径 {"timeStamp": "20220801110008","transIDO": "6ba9088c981b407fb38feasdf09","version": "1.0.0","signMethod": "md5","content": "{\"companyName\&quo…...

【uniapp 上传图片示例】

以下是 uniapp 上传图片的详细步骤示例&#xff1a; 定义一个方法&#xff0c;用于选择图片并上传&#xff1a; methods: {chooseImage() {uni.chooseImage({count: 1, // 最多选择的图片数量sizeType: [original, compressed], // 可以指定原图或压缩图sourceType: [album, …...

apache2配置文件 Require all granted是什么意思

修改apache2的配置文件 /etc/apache2/apache2.conf&#xff0c;需要增加网站代码的路径&#xff0c;下列配置是什么意思呢 <Directory "/var/www/html">Options FollowSymLinksAllowOverride AllRequire all granted </Directory> 1. Options Options …...

c/c++ 的一些知识

c 面向对象是一种思想&#xff0c;通常情况下都是以组合为主&#xff0c;也就是在子类里定义一个基类struct base_t {void (*method)(base_t *base_p); };struct children_t {int a;int b;base_t base;void (*method)(children_t *children_p); };children_t children_creat(i…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

从零开始了解数据采集(二十八)——制造业数字孪生

近年来&#xff0c;我国的工业领域正经历一场前所未有的数字化变革&#xff0c;从“双碳目标”到工业互联网平台的推广&#xff0c;国家政策和市场需求共同推动了制造业的升级。在这场变革中&#xff0c;数字孪生技术成为备受关注的关键工具&#xff0c;它不仅让企业“看见”设…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...