【NBUOJ刷题笔记】递推_递归+分治策略1

0. 前言

PS:本人并不是集训队的成员,因此代码写的烂轻点喷。。。本专题一方面是巩固自己的算法知识,另一方面是给NBU学弟学妹们参考解题思路(切勿直接搬运抄袭提交作业!!!)最后,该系列博客AC代码均以Java语言提交,C/C++的可以参考思路编写代码

1. 题目详情

1.1 题目一:王老师爬楼梯

1.1.1 题目信息

题目描述
王老师爬楼梯,他可以每次走1级或者2级或者3级楼梯,输入楼梯的级数,求不同的走法数。(要求递推求解)如果N很大,需要高精度计算。
输入要求
一个整数N,N<=1000。
输出要求
共有多少种走法。
输入样例:
10
输出样例
274
来源
NBUOJ

1.1.2 算法思路(递推=>动态规划)

本题是动态规划的入门题,可以让同学较好的从递推的思维转向动态规划思维
动态规划五步走(重要)

  1. 状态定义:这一步是相当重要的,定义一个合适的状态表示是解题的关键,我们假设dp[i]为走到第i级台阶的走法总数
  2. 状态转移方程:假设我们使用上一步骤的状态表示,那么我们需要思考 dp[i] 可以由哪些状态转移得到,根据题意王老师最后可以跨1步,也可以跨2步,也可以跨3步到达第i级台阶,因此可以得出状态转移方程为:dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3](其中i >= 4)
  3. 状态初始化:由于我们在i<4时直接套用该公式会造成数组越界,因此我们需要初始化dp[1]、dp[2]、dp[3]的值
  4. 填表顺序:根据我们的状态转移方程,我们发现dp[i]的值可以由前三项推出,因此填表顺序是从左往右填写的
  5. 返回值:根据题目含义我们需要输出第n级台阶的走法个数,因此我们填表完毕后需要返回的就是dp[n]

示例:我们假设输入为4进行举例:
image.png
即通俗来说,本题的递推思路就是将王老师走到第i级台阶的最后一步的步数进行分类,一共有三种情况:

  • case1(最后走了一步):dp[i - 1]
  • case2(最后走了两步):dp[i - 2]
  • case3(最后走了三步):dp[i - 3]

1.1.3 AC代码(Java实现)

NBUOJ上面Java带中文注释会报错!因此这里放了两个版本的代码,前面不带注释的可以直接跑OJ通过,后面的方便读者阅读

1.1.3.1 温馨提示

由于本题的n值最大为1000,因此当n值过大时会溢出整数的表示范围(无论是Java的long类型还是C++的long long都不行),因此本题需要借助 大数相加 的模板,不过咱们Java程序员可以使用标准库提供的 BigInteger ,下面简单介绍相关常用的API

API返回值说明
new BigInteger(String str)BigInteger实例使用字符串表示的数值构造实例
BigInteger.valueOf(long num)BigInteger实例使用long类型表示的数值构造实例
x.add(BigInteger y)BigInteger实例返回x和y所表示的大数相加的结果
x.multiply(BigInteger y)BigInteger实例返回x和y所表示的大数相乘的结果
1.1.3.2 不带注释版本
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();BigInteger[] dp = new BigInteger[n + 1];dp[1] = BigInteger.valueOf(1);dp[2] = BigInteger.valueOf(2);dp[3] = BigInteger.valueOf(4);for (int i = 4; i <= n; i++) {dp[i] = dp[i - 1].add(dp[i - 2]).add(dp[i - 3]);}System.out.println(dp[n]);}
}
1.1.3.3 带注释版本
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();// 1. 状态定义:dp[i]表示王老师走到第i级台阶的走法数BigInteger[] dp = new BigInteger[n + 1];// 2. 状态初始化dp[1] = BigInteger.valueOf(1);dp[2] = BigInteger.valueOf(2);dp[3] = BigInteger.valueOf(4);// 3. 状态转移方程for (int i = 4; i <= n; i++) {dp[i] = dp[i - 1].add(dp[i - 2]).add(dp[i - 3]);}// 4. 返回值System.out.println(dp[n]);}
}

1.1.4 扩展题

这里放一些跟本题类似的OJ题(读者可自行尝试解题):

  1. LeetCode70 爬楼梯:https://leetcode.cn/problems/climbing-stairs/
  2. LeetCode118 斐波那契数列:https://leetcode.cn/problems/pascals-triangle/

1.2 题目二:铺砖

1.2.1 题目信息

题目描述
对于一个2行N列的走道。现在用12或22的砖去铺满。问有多少种不同的方式(请用递推方式求解)。如果N很大,需要高精度计算。下图是一个2行17列的走道的某种铺法:
image.png
输入要求
一个整数N,N<=1000。
输出要求
共有多少种铺法。
输入样例
30
输出样例
715827883
来源
NBUOJ

1.2.2 算法思路(递归=>动态规划)

动态规划五步走(重要)

  1. 状态定义:这一步是相当重要的,定义一个合适的状态表示是解题的关键,我们假设dp[i]表示为2行i列的砖的铺法总数
  2. 状态转移方程:假设我们使用上一步骤的状态表示,那么我们需要思考 dp[i] 可以由哪些状态转移得到,根据题意我们可以根据最后一块砖的长度为1还是为2进行区分,因此可以得出状态转移方程为:dp[i] = dp[i - 1] + 2 * dp[i - 2](其中i >= 3)
  3. 状态初始化:由于我们在i<3时直接套用该公式会造成数组越界,因此我们需要初始化dp[1]、dp[2]的值
  4. 填表顺序:根据我们的状态转移方程,我们发现dp[i]的值可以由前两项推出,因此填表顺序是从左往右填写的
  5. 返回值:根据题目含义我们需要输出长度为n的铺砖方法数,因此我们填表完毕后需要返回的就是dp[n]

示例:也许上面的文字描述有点抽象,还是以画图进行举例:
image.png
相信此图一出,大家瞬间就明白公式:dp[i] = dp[i - 1] + 2 * dp[i - 2](其中i >= 3)的由来了,其实就是根据最后一块砖的摆法划分出不同的方案

1.2.3 AC代码(Java实现)

NBUOJ上面Java带中文注释会报错!因此这里放了两个版本的代码,前面不带注释的可以直接跑OJ通过,后面的方便读者阅读

1.2.3.1 温馨提示

由于本题的n值最大为1000,因此当n值过大时会溢出整数的表示范围(无论是Java的long类型还是C++的long long都不行),因此本题需要借助 大数相加 的模板,不过咱们Java程序员可以使用标准库提供的 BigInteger ,下面简单介绍相关常用的API

API返回值说明
new BigInteger(String str)BigInteger实例使用字符串表示的数值构造实例
BigInteger.valueOf(long num)BigInteger实例使用long类型表示的数值构造实例
x.add(BigInteger y)BigInteger实例返回x和y所表示的大数相加的结果
x.multiply(BigInteger y)BigInteger实例返回x和y所表示的大数相乘的结果
1.2.3.2 不带注释版本
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();BigInteger[] dp = new BigInteger[n + 1];dp[1] = new BigInteger("1");dp[2] = new BigInteger("3");for (int i = 3; i <= n; i++) {dp[i] = dp[i - 1].add(dp[i - 2].multiply(new BigInteger("2")));}System.out.println(dp[n]);}
}
1.2.3.3 带注释版本
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();// 1. 状态定义:dp[i]表示2行i列的铺法数BigInteger[] dp = new BigInteger[n + 1];// 2. 状态初始化dp[1] = new BigInteger("1");dp[2] = new BigInteger("3");// 3. 状态转移方程for (int i = 3; i <= n; i++) {dp[i] = dp[i - 1].add(dp[i - 2].multiply(new BigInteger("2")));}// 4. 返回值System.out.println(dp[n]);}
}

1.3 题目三:整数分割(1)

1.3.1 题目信息

题目描述
N年M月O日CX和ZJS同学在刚学玩整数加减后,就在那里比试谁厉害。比试内容是:将一个正整数N拆成若干个正整数的和。看谁拆的多谁就赢。为了赢得比赛,CX就向你求助一个整数的所有拆分方法。
输入要求
输入N( 1 <= N <= 50 )。
输出要求
对于N的拆分方法,请以字典序排列(数字越大越排前面)。每一种拆分之间以一个空格分开。
输入样例
7
输出样例:
image.png
来源
NBUOJ

1.3.2 算法思路(暴力DFS深搜)

这里直接给大家介绍我的算法思路了:观察输出用例我们可以发现,数字的排列顺序就是从大到小的,因此我们可以从大到小不断添加元素(如果>=target就回溯,其中=target的时候就进行输出)这样我们就可以不重不漏的找到所有的拆分情况
算法步骤(重要)

  1. 我们设计一个递归函数dfs(int[] numArr, int curLen, int curSum, int target, int curNum)用于查找所有的拆分情况,各个参数含义如下所示:
  • numArr:保存已经被拆分的数字
  • curLen:记录numArr的元素个数
  • curSum:当前numArr元素之和(即已经拆分元素之和)
  • target:拆分目标和
  • curNum:当前需要进行匹配的元素
  1. 然后从[curNum —— 1]不断从大到小尝试添加元素到拆分数组,直到所有情况都搜索完毕

1.3.3 AC代码(Java实现)

NBUOJ上面Java带中文注释会报错!因此这里放了两个版本的代码,前面不带注释的可以直接跑OJ通过,后面的方便读者阅读

1.3.3.1 优化点

本题如果使用Java语言是比较不容易过的,我的代码做优化的地方有如下几点:

  • 输入输出优化:输入流将Scanner换成了BufferReader、输出流将System.out换成了PrintWriter
  • 数据结构优化:使用List集合保存拆分元素,频繁调用add和remove方法,开销太大,因此我换用数组保存拆分元素;并且由于输入组数很多,打印频率非常高,于是我使用StringBuildler保存全部的结果,最后统一打印
  • 函数参数优化:此时终于将时间变成了 2016ms!最后一步优化就是将StringBuilder和numArr数组从函数参数中抽离到成员变量,省去了函数调用过程中传参的开销,至此程序优化到1703ms!

image.png

1.3.3.2 不带注释版本
package week1.blog_code.exer1002;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;public class Main {private static int[] numArr = new int[51];private static StringBuilder stringBuilder = new StringBuilder();public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));PrintStream printStream = new PrintStream(System.out);String line = null;while ((line = reader.readLine()) != null) {int target = Integer.parseInt(line);numArr = new int[51];stringBuilder = new StringBuilder();dfs(numArr, 0, 0, target, target);printStream.print(stringBuilder.toString());}}public static void dfs(int[] numArr, int curLen, int curSum, int target, int curNum) {if (curSum == target) {for (int i = 0; i < curLen - 1; i++) {stringBuilder.append(numArr[i] + " ");}stringBuilder.append(numArr[curLen - 1] + "\n");return;}for (int i = curNum; i >= 1; i--) {if (curSum + i <= target) {numArr[curLen] = i;dfs(numArr,curLen + 1, curSum + i, target, i);}}}
}
1.3.3.3 带注释版本
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;public class Main {private static int[] numArr = new int[51];private static StringBuilder stringBuilder = new StringBuilder();public static void main(String[] args) throws IOException {// 缓冲输入流(高级版平替scanner)BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));// 打印流(高级版平替System.out.print)PrintStream printStream = new PrintStream(System.out);String line = null;while ((line = reader.readLine()) != null) {int target = Integer.parseInt(line);numArr = new int[51];stringBuilder = new StringBuilder();dfs(numArr, 0, 0, target, target);printStream.print(stringBuilder.toString());}}/*** @param numArr 当前拆分元素构成的数组* @param curLen 当前拆分数组长度* @param curSum 当前拆分方法之和* @param target 目标和* @param curNum 当前遍历到的数*/public static void dfs(int[] numArr, int curLen, int curSum, int target, int curNum) {// 出口:当前拆分和==目标和就不用继续搜索if (curSum == target) {for (int i = 0; i < curLen - 1; i++) {stringBuilder.append(numArr[i] + " ");}stringBuilder.append(numArr[curLen - 1] + "\n");return;}for (int i = curNum; i >= 1; i--) {if (curSum + i <= target) {// 将i值加入拆分数组中numArr[curLen] = i;// 继续深搜dfs(numArr,curLen + 1, curSum + i, target, i);}}}
}

1.3.4 扩展题

这里放一些跟本题类似的OJ题(读者可自行尝试解题):

  1. LeetCode46 全排列:https://leetcode.cn/problems/permutations/description/
  2. LeetCOde78 子集:https://leetcode.cn/problems/subsets/description/

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

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

相关文章

【消息队列开发】 虚拟主机设计——操作绑定

文章目录 &#x1f343;前言&#x1f332;添加绑定&#x1f333;删除绑定⭕总结 &#x1f343;前言 本次开发任务&#xff1a; 实现对绑定的添加与绑定 &#x1f332;添加绑定 对于绑定的操作相较于前面对交换机和队列的操作就会麻烦一点了 我们分为以下七步来实现&#x…

【xr806开发板使用】连接wifi例程实现

##开发环境 win10 WSL ##1、环境配置 参考&#xff1a;https://aijishu.com/a/1060000000287513 首先下载安装wsl 和ubuntu https://docs.microsoft.com/zh-cn/windows/wsl/install &#xff08;1&#xff09;安装repo&#xff1a; 创建repo安装目录&#xff1a; mkdir ~/…

AI预测福彩3D第15弹【2024年3月21日预测--新算法重新开始计算第12次测试】

今天继续对第一套算法进行测试&#xff0c;废话不多说&#xff0c;直接上结果。 最终&#xff0c;经过研判分析&#xff0c;2024年3月21日福彩3D的七码预测结果如下&#xff1a; 百位&#xff1a;5 4 7 1 2 0 6 十位&#xff1a;3 5 1 2 0 4 6(9换6) 个位&#xff1a;2 0 1 3 4…

修改网站源码,给电子商城的商品添加图片时商品id为0的原因

修改网站源码&#xff0c;给电子商城的商品添加图片时商品id为0的原因。花了几个小时查找原因。后来&#xff0c;由于PictureControl.class.php是复制CourseControl.class.php而来&#xff0c;于是对比了这两个文件&#xff0c;在CourseControl.class.php找到了不一样的关键几条…

【算法】差分算法(空调)

可用于求一个数组要变为另一个数组最少要改变多少次的次数 Farmer John 的 N 头奶牛对他们牛棚的室温非常挑剔。 有些奶牛喜欢温度低一些&#xff0c;而有些奶牛则喜欢温度高一些。 Farmer John 的牛棚包含一排 N 个牛栏&#xff0c;编号为 1…N&#xff0c;每个牛栏里有一头…

【Linux系列】计算机系统中的架构与发行版:理解与区分

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

一文读懂MES和ERP的区别

MES&#xff08;Manufacturing Execution System&#xff09;系统是制造执行系统&#xff0c;位于上层的计划管理系统与生产过程的直接工业控制系统之间&#xff0c;是面向车间层的管理信息系统&#xff0c;能够对整个车间制造过程进行优化&#xff0c;实时收集生产过程中的数据…

LeetCode-60题:排列序列解法二(原创)

【题目描述】 给出集合 [1,2,3,...,n]&#xff0c;其所有元素共有 n! 种排列。按大小顺序列出所有排列情况&#xff0c;并一一标记&#xff0c;当 n 3 时, 所有排列如下&#xff1a;"123" 、"132" 、"213" 、"231"、"312"、…

数字化转型:传统行业的新出路?

近年来&#xff0c;数字化转型已成为各行各业的热议话题。许多传统行业正面临着巨大的挑战&#xff0c;例如市场竞争加剧、成本上升、利润率下降等。数字化转型被视为传统行业破局的关键。那么&#xff0c;数字化转型究竟是不是传统行业的新出路呢&#xff1f; 传统行业面临的挑…

Qt 坐标位置转换

Qt 坐标位置转换 文章目录 Qt 坐标位置转换常见的位置坐标转换Qt窗体中常用坐标的区别与获取途径当前光标相对于屏幕的绝对位置当前光标相对于当前窗口的位置鼠标事件发生的位置窗体的位置判断鼠标光标是否悬浮在某个子控件上 从事Qt快一年了 &#xff0c;在做坐标转换的时候容…

OpenCV4.9.0在Android 开发简介

查看&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;使用 Clojure 进行 OpenCV 开发简介 下一篇&#xff1a;暂无 引言&#xff1a; OpenCV是一个跨平台计算机视觉库&#xff0c;广泛用于图像处理、计算机视觉和机器学习等领域…

python网络爬虫实战教学——urllib的使用(3)

文章目录 专栏导读1、urlsplit2、urlunsplit3、urljoin4、urlencode 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对大学生、…

日志集中审计系列(1)--- LogAuditor接收DAS设备syslog日志

日志集中审计系列(1)--- LogAuditor接收DAS设备syslog日志 前言拓扑图设备选型组网需求配置思路操作步骤结果验证前言 近期有读者留言:“因华为数通模拟器仅能支持USG6000V的防火墙,无法支持别的安全产品,导致很多网络安全的方案和产品功能无法模拟练习,是否有真机操作的…

群发短信后的实际效果统计分析

群发短信后的实际效果统计分析是评估短信营销效果的关键环节&#xff0c;有助于企业了解短信发送的效果&#xff0c;并据此优化营销策略。以下是对群发短信实际效果统计分析的详细探讨&#xff1a; 首先&#xff0c;企业需要关注短信的发送成功率。这包括短信是否成功发送至目标…

TinTin Web3 Bounty 挑战杯开启,Sui 向你发出挑战邀请!

2024 年开年最火的是什么&#xff1f; 对 Web3 来说&#xff0c;Bounty 任务应该是普通人获得行业“一杯羹”的重要捷径&#xff01; 通过深入学习各类 Web3 技术&#xff0c;凭借实战锻炼开发创新项目&#xff0c;就有机会获得各大生态项目方的 Bounty 奖励。 TinTinLand 社…

TSINGSEE青犀数字化、智能化视频技术推动森林防火智慧监管

一、背景分析 中央网络安全和信息化委员会印发《“十四五”国家信息化规划》&#xff0c;明确指出“提升林草生态网络感知能力&#xff0c;完善生态系统保护成效数字化监测评估体系”。这为数字化系统建设引领了方向&#xff0c;中国林业信息化建设迈入了新的阶段&#xff0c;全…

HTML5语法总结

文章目录 一.HTML基本框架二.标题标签三.段落标签四.换行与水平线标签五.文本格式化标签(加粗、倾斜、下划线、删除线)六.图像标签扩展&#xff1a;相对路径,绝对路径与在线网址 七.超链接标签八.音频标签九.视频标签十.列表标签十一.表格标签扩展&#xff1a;表格结构标签合并…

[C/C++] -- 链表

C/C 中链表是一种常见的数据结构&#xff0c;用于存储和组织数据。链表由节点&#xff08;Node&#xff09;组成&#xff0c;每个节点包含数据和指向下一个节点的指针。链表相对于数组的优势在于可以动态地分配内存&#xff0c;插入和删除操作效率高&#xff0c;但访问元素的随…

MacOS 电脑如何通过自带terminal终端连接PostgreSQL

1、安装Postgre SQL客户端工具 brew install postgresql 2、连接到PostgreSQL &#xff08;1&#xff09;创建远程连接 psql -h hostname -U username -d database 其中&#xff0c;hostname 是 PostgreSQL 服务器的主机名或 IP 地址&#xff0c;username 是您的 PostgreS…

机器学习 - 预测训练模型

接着上篇博客机器学习-训练模型做进一步说明。 There are three things to make predictions (also called performing inference) with a PyTorch model: Set the model in evaluation mode (model.eval())Make the predictions using the inference mode context manager (…