暴力递归到动态规划(一)

news2024/11/26 4:30:55

请添加图片描述
⭐️前言⭐️

动态规划是一个很难的模块,如果一道动态规划的题目直接去推出动态转移方程来解题,是很难的,所以应该先想出暴力解决的方法,再去用空间换时间优化,得出动态规划的解法。

🍉欢迎点赞 👍 收藏留言评论 📝私信必回哟😁

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉博客中涉及源码及博主日常练习代码均已上传GitHub


请添加图片描述

📍内容导读📍

  • 🍅机器人走路
  • 🍅拿牌游戏

🍅机器人走路

题目:
假设有排成一行的N个位置记为1~N,N一定大于或等于2
开始时机器人在其中的M位置上(M一定是1~N中的一个)
如果机器人来到1位置,那么下一步只能往右来到2位置;
如果机器人来到N位置,那么下一步只能往左来到N-1位置;
如果机器人来到中间位置,那么下一步可以往左走或者往右走;
规定机器人必须走K步,最终能来到P位置(P也是1~N中的一个)的方法有多少种
给定四个参数 N、M、K、P,返回方法数

题解思路1:
暴力递归,列举所有可能性,确定结束递归的条件,把所有的可能性加起来就是结果。
代码实现:

public class RobotWalk {
    /**
     * @param N  有1~N个位置
     * @param start   起始位置
     * @param aim   目标位置
     * @param k    要走k步
     * @return
     */
    public static int ways(int N,int start,int aim,int k) {
        return process(start,k,aim,N);
    }

    /**
     * @param cur  目前所在位置
     * @param rest  剩余步数
     * @param aim   目标位置
     * @param N   有N个位置
     * @return
     */
    public static int process(int cur,int rest,int aim,int N) {
        if(rest==0) {
            return cur==aim?1:0;
        }
        if(cur==1) {
            return process(2,rest-1,aim,N);
        }
        if(cur==N) {
            return process(N-1,rest-1,aim,N);
        }
        return process(cur-1,rest-1,aim,N)+process(cur+1,rest-1,aim,N);
    }
}

题解思路2:
记忆化搜索、从顶向下的动态规划
假设一开始在位置3,剩余4步,那么下一步可能走到位置2,剩余3步;或者位置4,剩余3步;这两个位置都能走到位置3,剩余2步,位置3到达aim的可能性只由该位置决定,与怎样到达位置3的没关系,所以为了减少重复计算,可以增加缓存表,把之前的结果记录下来,当后续其他分支又走到该位置时,可以直接使用不需要再计算。
代码实现:

public class RobotWalk {
    public static int ways2(int N,int start,int aim,int k) {
        int[][] dp=new int[N+1][k+1];
        for (int i = 0; i <=N; i++) {
            for (int j = 0; j <=k; j++) {
                dp[i][j]=-1;
            }
        }
        return process2(start,k,aim,N,dp);
    }

    public static int process2(int cur, int rest, int aim, int N, int[][] dp) {
        if(dp[cur][rest]!=-1) {
            return dp[cur][rest];
        }
        int ans=0;
        if(rest==0) {
            ans=cur==aim?1:0;
        }else if(cur==1) {
            ans=process2(2,rest-1,aim,N,dp);
        }else if(cur==N) {
            ans=process2(N-1,rest-1,aim,N,dp);
        }else {
            ans=process2(cur-1,rest-1,aim,N,dp)+process2(cur+1,rest-1,aim,N,dp);
        }
        dp[cur][rest]=ans;
        return ans;
    }
}

题解思路3:
动态规划最终版本,假设共有5个位置,2为起始位置,4为目标位置,需要走6步,那动态转移结果如下:
在这里插入图片描述

即求cur=2,rest=6位置的结果,cur=1时,结果依赖于与cur=2,rest-1位置的结果;
cur=5时,结果依赖于cur=4,rest-1位置的结果;中间位置的结果,依赖于(cur-1,rest-1)+(cur+1,rest-1)位置的结果。根据以上的规则,来完成dp表的填写,最终返回(2,6)位置的结果即可。
代码实现:

public class RobotWalk {  
    public static int ways3(int N,int start,int aim,int k) {
        int[][] dp=new int[N+1][k+1];
        dp[aim][0]=1;
        for (int rest = 1; rest <=k ; rest++) {
            dp[1][rest]=dp[2][rest-1];
            for (int cur = 2; cur < N; cur++) {
                dp[cur][rest]=dp[cur-1][rest-1]+dp[cur+1][rest-1];
            }
            dp[N][rest]=dp[N-1][rest-1];
        }
        return dp[start][k];
    }
}

结果测试:
在这里插入图片描述

🍅拿牌游戏

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

题解思路1:
拿牌有先手和后手两种姿态:
先手拿牌,所能拿到的最大分数为
1.arr[L]+后手在[L+1,R]范围取到的最小值(因为是后手所以取到的只能是最小值)
2.arr[R]+后手在[L,R-1]范围内取到的最小值
1、2中的最大值。
如果只剩一张牌,那就直接拿走。

后手拿牌,所能拿到的最大分数为
1.先手在[L+1,R]范围内的最大值
2.先手在[L,R-1]范围内的最大值
1、2中的最小值,因为为后手取牌,所以得到的结果肯定为较小的结果。
如果只剩一张牌,那么只能拿到0。

代码实现:

public class CardsInLine {
    // 根据规则,返回获胜者的分数
    public static int win1(int[] arr) {
        if(arr==null||arr.length==0) {
            return 0;
        }
        int first=f1(arr,0,arr.length-1);
        int second=g1(arr,0,arr.length-1);
        return Math.max(first,second);
    }
    
    // arr[L..R] 先手获得的最好分数返回
    public static int f1(int[] arr, int L, int R) {
        if(L==R) {
            return arr[L];
        }
        int p1=arr[L]+g1(arr,L+1,R);
        int p2=arr[R]+g1(arr,L,R-1);
        return Math.max(p1,p2);
    }
    
    // arr[L..R] 后手获得的最好分数返回
    public static int g1(int[] arr, int L, int R) {
        if(L==R) {
            return 0;
        }
        int p1=f1(arr,L+1,R);  // 对手拿走了L位置的数
        int p2=f1(arr,L,R-1);  // 对手拿走了R位置的数
        return Math.min(p1,p2);
    }
}

题解思路2:
由下图的依赖关系可得,还是会有重复的计算,所以可以通过缓存来减少重复的计算。
在这里插入图片描述

代码实现:

public class CardsInLine {
    public static int win2(int[] arr) {
        if(arr==null||arr.length==0) {
            return 0;
        }
        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=f2(arr,0,arr.length-1,fmap,gmap);
        int second=g2(arr,0,arr.length-1,fmap,gmap);
        return Math.max(first,second);
    }

    public static int f2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {
        if(fmap[L][R]!=-1) {
            return fmap[L][R];
        }
        int ans=0;
        if(L==R) {
            ans=arr[L];
        }else {
            int p1=arr[L]+g2(arr,L+1,R,fmap,gmap);
            int p2=arr[R]+g2(arr,L,R-1,fmap,gmap);
            ans=Math.max(p1,p2);
        }
        fmap[L][R]=ans;
        return ans;
    }

    public static int g2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {
        if(gmap[L][R]!=-1) {
            return gmap[L][R];
        }
        int ans=0;
        if(L!=R) {
            int p1=f2(arr,L+1,R,fmap,gmap); // 对手拿走了L位置的数
            int p2=f2(arr,L,R-1,fmap,gmap); // 对手拿走了R位置的数
            ans=Math.min(p1,p2);
        }
        gmap[L][R]=ans;
        return ans;
    }
}

题解思路3:
状态转移表,根据暴力递归解法,来推断出表格中的结果。
假设一组数据为[7,4,16,15,1],那么对应的两张表的状态如下:
在这里插入图片描述

在f表中,如果L == R,即返回该位置的值,而在g表中,作为后手姿态,L==R时,只能获得0.
因为L<=R,所以表格中不合法区域直接用×排除
最终即判断f表中(0,4)与g表中(0,4)两个位置的结果大小,返回较大的即可。

其余位置的填写规则,可以由暴力递归的解法来得出。
比如g(0,1)位置,其结果依赖于f(1,1)与f(0,0)两个位置的结果;f(0,1)位置,其结果依赖于g(1,1)与g(0,0)两个位置的结果;即可得出规则,填写结果依赖于另一张表对应位置的左、下位置的结果。
代码实现:

public class CardsInLine {
    public static int win3(int[] arr) {
        if(arr==null||arr.length==0) {
            return 0;
        }
        int N=arr.length;
        int[][] fmap=new int[N][N];
        int[][] gmap=new int[N][N];
        for (int i = 0; i < N; i++) {
            fmap[i][i]=arr[i];
        }
        for (int i = 1; i < N; i++) {
            int L=0;
            int R=i;
            while (R<N) {
                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],gmap[L][R-1]);
                L++;
                R++;
            }
        }
        return Math.max(fmap[0][N-1],gmap[0][N-1]);
    }
}

⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

请添加图片描述

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

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

相关文章

【Java设计模式】—— 享元模式概述和示例

目录 概述享元模式的定义与特点享元模式的结构与实现1. 模式的结构2. 模式的理论实现 享元模式的应用实例代码实现 享元模式的应用场景享元模式的在实际工作中的应用参考文献 概述 在面向对象程序设计过程中&#xff0c;有时会面临要创建大量相同或相似对象实例的问题。创建那…

Linux命令学习之mkdir、rmdir和rm

这篇文章想要学习一下目录的创建&#xff08;mkdir&#xff09;、空目录的删除&#xff08;rmdir&#xff09;、非空目录的删除&#xff08;rm&#xff09;。 mkdir mkdir是新建目录的命令。man mkdir看一下mkdir的使用说明。 按q键退出帮助说明。 mkdir /learnwell在根目录…

贷款业务-贷款模式

参考文章 多流量模式下的系统设计- 呱说产品自营 互联网金融之信贷三部曲&#xff1a;贷中 导读 互联网的玩法&#xff0c;基本都是流量为王。网上也有种说法&#xff0c;互联网平台&#xff0c;最终都会走上金融业务&#xff0c;一般都是指贷款业务。 但是&#xff0c;做…

PLC信号发生器(博途SCL)

信号发生器的应用请参看下面的博客文章,在演示分析滤波器的作用时,我们需要对信号进行叠加处理。 博途PLC滤波指令 Filter_PT1、Filter_PT2、Filter_DT1详细使用说明(含Simulink+博途PLC仿真)_RXXW_Dor的博客-CSDN博客博途S7-1200/1500PLC的PID控制和详细使用说明,请参看…

计算机组成原理---第四章 指令系统习题详解版

&#xff08;一&#xff09;课内例题 4.1 4.2 具体分析&#xff0c;4.2 中&#xff0c; 因为只有一行&#xff1a;单字长 二地址&#xff1a;源寄存器、目标寄存器 操作码看OP&#xff08;15-91&#xff09; 然后按操作数的物理位置来区别RR型&#xff0c;RS型&#xf…

代码随想录训练营Day58| 739. 每日温度 496.下一个更大元素 I

目录 学习目标 学习内容 739. 每日温度 496.下一个更大元素 I 学习目标 739. 每日温度 496.下一个更大元素 I 学习内容 739. 每日温度 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/daily-temperatures/ class Solution:def da…

Excel VBA编程

前两天我朋友找我帮忙&#xff0c;让给excel做个按钮功能&#xff0c;方便他们表格统计&#xff0c;我晚上研究了一下给他做出来了&#xff0c;这里记录一下入门操作和大概思路&#xff0c;具体怎么写VBA的代码我就不记录了&#xff0c;那玩意儿看教程去吧&#xff0c;也不是这…

【网络】socket套接字

1.理解IP 每台主机都有自己的IP地址&#xff0c;所以当数据从一台主机传输到另一台主机就需要IP地址。报头中就会包含源IP和目的IP。源IP地址&#xff1a;发送数据报那个主机的IP地址&#xff0c;目的IP地址&#xff1a;想发送到的那个主机的IP地址我们把数据从一台主机传递到…

C语言-const关键字

1.const 关键字 const 是 constant 的缩写&#xff0c;const 在实际编程中用得比较多&#xff0c;意思是“恒定不变的”&#xff0c;它是定义只读变量的关键字&#xff0c;或者说 const 是定义常变量的关键字。 (可读&#xff0c;不可写) const 可以修饰变量&#xff0c;数组&a…

光明区马田大围城市更新旧改项目--华润集团

【华润光明马田大围旧改一期项目基础信息】 项目开发商&#xff1a;华润集团 占 地 面 积&#xff1a;约110151㎡ 建 筑 面 积&#xff1a;待专规图 物 业 类 型&#xff1a;华润一期范围内的待拆迁物业 项 目 进 度&#xff1a;直接确权阶段&#xff0c;即将立项 【项目现…

c++ new 源码学习一下

之前有一篇文章介绍了 new 的一些用法 c new 在指定内存上创建对象&#xff0c;今天结合源码来学习一下 new 更详细的用法。相关的源码&#xff1a;gcc git 1&#xff0c;void* operator new (std::size_t size); 我们可以在头文件<new>里看到它的原型&#xff1a; _G…

J2EE 技术的企业人事管理系统的设计与实现(论文+源码)_kaic

目 录 一、引 言 二、相关技术概述 (一) 基于 MVC 设计模式的 J2EE 技术 (二) MySql 数据库管理系统 (三) 基于 B/S 结构的系统安全 三、企业人事管理系统总体设计 (一) 系统需求分析 1.功能结构分解 2.员工档案管理模块 3.员工工资管理模块 (二) 系统总体结构设计 四、企业人…

2023-06-03 陕西省技能大赛 crypto 复现

文章目录 奇怪的sar题目描述&#xff1a;题目分析&#xff1a; Ham3题目描述&#xff1a;题目分析&#xff1a; BigDataEnc题目描述&#xff1a;题目分析&#xff1a; 奇怪的sar 题目描述&#xff1a; from Crypto.Util.number import *key flag{**********}bits 1024 msg …

ESP32+U8G2库显示中文

这里写自定义目录标题 在使用esp32u8g2oled进行中文先显示例程 废话不说&#xff0c;直接上代码 #include <Arduino.h> #include <U8g2lib.h> //软件SPI U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock/ 14, / data/ 13, / cs/ 15, / dc/ 7, / r…

【AI绘图】三、stable diffussion操作界面介绍以及如何使用

上一篇&#xff1a;【AI绘图】二、stable diffusion环境准备与安装 stable diffusion操作界面介绍 操作界面总共分为5大块 1.模型选择区&#xff1b;点击跳转 2.功能区域&#xff1b;点击跳转 3.提示词输入&#xff1b;点击跳转 4.参数设置区&#xff1b;点击跳转 5.出图区点击…

计算机基础--->操作系统(1)

文章目录 简单说说操作系统操作系统的主要功能用户态和内核态为什么要有用户态和内核态&#xff1f;只有一个内核态不行吗&#xff1f;用户态和内核态是如何切换的系统调用系统调用过程 简单说说操作系统 操作系统是一种系统软件&#xff0c;它管理计算机硬件资源和提供服务给应…

Jenkins+Docker+SpringCloud微服务持续集成之集群部署

一、集群部署流程说明 环境配置 #环境配置 hostnamectl set-hostname web2-server && su systemctl stop firewalld systemctl disable firewalldvim /etc/selinux/config SELINUXdisabledvim /etc/resolv.conf nameserver 114.114.114.114#安装依赖包 yum install -y…

如何通过python写接口自动化脚本对一个需要调用第三方支付的报名流程进行测试?

对于需要调用第三方支付的报名流程进行接口自动化测试&#xff0c;可以通过以下步骤来编写Python代码&#xff1a; 如果你想学习自动化测试&#xff0c;我这边给你推荐一套视频&#xff0c;这个视频可以说是B站百万播放全网第一的自动化测试教程&#xff0c;同时在线人数到达1…

微信小程序开发实战 ②③(全局数据共享)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

Nginx:Nginx网站服务(一)

Nginx&#xff1a;Nginx网站服务 一、编译安装Nginx服务1.1 关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下1.2 安装依赖包1.3 创建运行用户、组&#xff08;Nginx 服务程序默认以 nobody 身份运行&#xff0c;建议为其创建专门的用户账号&#xff0c;以便更准确地…