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

news2024/11/13 11:12:46

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.coloradmin.cn/o/1533260.html

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

相关文章

QT5.14.2 Qt布局调和术:精妙UI设计背后的自适应魔法

欢迎来到Qt世界的一角&#xff0c;今天我们要探索的是Qt中的布局管理以及控件自适应大小调整的艺术。在这篇文章中&#xff0c;我们不仅会讨论理论&#xff0c;还会一起动手实践&#xff0c;弄清楚如何打造出既美观又实用的用户界面。 一、布局管理概览 布局管理是Qt中的核心概…

2024腾讯云优惠券领取_云服务器代金券_优惠券查询和使用方法

腾讯云代金券领取渠道有哪些&#xff1f;腾讯云官网可以领取、官方媒体账号可以领取代金券、完成任务可以领取代金券&#xff0c;大家也可以在腾讯云百科蹲守代金券&#xff0c;因为腾讯云代金券领取渠道比较分散&#xff0c;腾讯云百科txybk.com专注汇总优惠代金券领取页面&am…

HTML5语法总结

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

表情生成器微信小程序版

1.纯前端输出&#xff0c;无需后台&#xff0c;无需域名&#xff0c;速度杠杠快&#xff01; 2.完美支持微信端和抖音端&#xff1b; 3.双端均支持配置开启流量主广告&#xff0c;包括&#xff1a;激励视频广告、插屏广告、banner广告、原生广告、封面广告等&#xff1b; 4.…

【蓝桥杯入门记录】继电器、蜂鸣器及原理图分析

一、继电器、继电器概述 &#xff08;1&#xff09;蜂鸣器原理 蜂鸣器的发声原理由振动装置和谐振装置组成&#xff0c;而蜂鸣器又分为无源他激型与有源自激型&#xff0c;蜂鸣器的发声原理为: 1、无源他激型蜂鸣器的工作发声原理是&#xff1a;方波信号输入谐振装置转换为声…

阿里云服务器2核4G服务器收费价格表,1个月和一年报价

阿里云2核4G服务器多少钱一年&#xff1f;2核4G服务器1个月费用多少&#xff1f;2核4G服务器30元3个月、85元一年&#xff0c;轻量应用服务器2核4G4M带宽165元一年&#xff0c;企业用户2核4G5M带宽199元一年。本文阿里云服务器网整理的2核4G参加活动的主机是ECS经济型e实例和u1…

【unity实战】时间控制 昼夜交替 四季变化 天气变化效果

最终效果 文章目录 最终效果日期季节控制时间昼夜交替素材如果没有天空盒&#xff0c;需要自己配置新增SkyboxBlendingShader.shader&#xff0c;控制天空盒平滑过渡交替变化 下雨下雨粒子效果控制雨一直跟随玩家&#xff0c;但是旋转不跟随控制不同天气 源码完结 日期季节控制…

C++ —— 内存管理

目录 1. C内存分布 2. C 内存管理方式 2.1 new 和 delete 操作内置类型 2.2 new 和 delete 操作自定义类型 3. operator new与operator delete函数 4. new和delete的实现原理 5. malloc/free 和 new/delete 的区别 1. C内存分布 首先看一段代码&#xff1a; int globalV…

短视频矩阵系统源头技术开发--每一次技术迭代

短视频矩阵系统源头开发3年的我们&#xff0c;肯定是需求不断的迭代更新的&#xff0c;目前我们已经迭代了3年之久&#xff0c;写技术文章已经写了短视频矩阵系统&#xff0c;写了3年了&#xff0c;开发了3年了 短视频矩阵获客系统是一种基于短视频平台的获客游戏。短视频矩阵系…

18个惊艳的可视化大屏(第29辑):机械自动化设备仪器

当涉及到机械自动化生产管理时&#xff0c;可视化大屏可以提供以下九个价值&#xff1a; 实时监控 可视化大屏可以实时显示生产线上的各个环节和设备的运行状态。运营人员可以通过大屏实时监控生产线的生产效率、设备运行状况等关键指标&#xff0c;及时发现并解决问题&#…

面试笔记——Redis(集群方案:主从复制、哨兵模式和分片集群)

主从复制 在 Redis 主从集群中&#xff0c;一个主节点&#xff08;Master&#xff09;负责处理客户端的读写请求&#xff0c;而多个从节点&#xff08;Slave&#xff09;则负责复制主节点的数据&#xff0c;并对外提供读取服务——解决高并发问题。 主节点&#xff08;Master&…

echart多折线图堆叠 y轴和实际数据不对应

当使用 ECharts 绘制堆叠折线图时&#xff0c;有时会遇到 y 轴与实际数据不对应的问题。 比如明明值是50&#xff0c;但折线点在y轴的对应点却飙升到了二百多 解决办法&#xff1a; 查看了前端代码发现在echart的图表中有一个‘stack’的属性&#xff0c;尝试把他删除之后y轴的…

带大家深入理解redis的主从复制

为何需要主从复制 Redis作为开发过程中经常用到的缓存数据库&#xff0c;我们在开发过程中可能会遇到各种各样的问题&#xff0c;比如&#xff1a; Redis的读并发量太大怎么办&#xff1f; 单机版的Redis挂掉怎么办&#xff1f; 我们不仅需要并发&#xff0c;更需要安全&…

力扣热门算法题 52. N 皇后 II,53. 最大子数组和,54. 螺旋矩阵

52. N 皇后 II&#xff0c;53. 最大子数组和&#xff0c;54. 螺旋矩阵&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.20 可通过leetcode所有测试用例。 目录 52. N 皇后 II 解题思路 完整代码 Python Java 53. 最大子数组…

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

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

UE4 Json事件设置Asset值

通过Json事件来设置&#xff0c;比如骨骼网格体&#xff08;换皮&#xff09;等等

用Python直接获取Word文档页数、字数、段落数、节数等信息

计算 Word 文档的页数、字数等信息是出版、学术和内容管理等领域的一项基本任务。准确的页数和字数对于评估文档长度、估算印刷成本、分析文本复杂性以及确保符合格式化指南至关重要。逐个预览文档查看相关信息是非常麻烦的事情&#xff0c;我们可以在不预览文档的情况下&#…

Day74:WEB攻防-机制验证篇重定向发送响应状态码跳过步骤验证码回传枚举

目录 验证码突破-回传显示&规律爆破 某目标回显显示 某APP验证码爆破 验证目标-重定向用户&重定向发送 某CMS重定向用户 某CMS重定向发送 验证逻辑-修改响应包&跳过步骤URL 某APP修改响应包 某APP跳过步骤URL 实战SRC验证逻辑挖掘分享案例 短信验证码回…

集成学习 | 集成学习思想:Boosting

目录 二. Boosting思想1. Adaboost 算法1.1 Adaboost算法构建流程1.2 sklearn库参数说明 2. Gradient Boosting 算法2.1 Gradient Boosting算法构建流程2.2 Gradient Boosting算法的回归与分类问题2.2.1 Gradient Boosting回归算法均方差损失函数绝对误差损失函数 2.2.2 Gradie…

使用 Dify 和 AWS Bedrock 玩转 Anthropic Claude 3

本篇文章&#xff0c;聊聊怎么比较稳定的使用 Anthropic Claude 3&#xff0c;以及基于目前表现非常好的模型&#xff0c;来做一些有趣的 AI Native 小工具。 写在前面 在实际体验了半个多月&#xff0c;月初上线的 Anthropic Claude Pro 后&#xff0c;发现 Claude 3 系列模…