【冲击蓝桥篇】动态规划(上):真题实战+思路解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 【冲击蓝桥篇】动态规划(上):真题实战+思路解析

2023年蓝桥杯Java组b组I:


题目一:接龙数组


题目描述:

对于一个长度为K的整数数列:A1, A2, ..., AK,我们称之为接龙数列当且仅当Ai的首位数字恰好等于Ai−1的末位数字(2 ≤ i ≤ K)。例如12, 23, 35, 56, 61, 11是接龙数列;12, 23, 34, 56不是接龙数列,因为56的首位数字不等于34的末位数字。所有长度为1的整数数列都是接龙数列。现在给定一个长度为N的数列A1, A2, ..., AN,请你计算最少从中删除多少个数,可以使剩下的序列是接龙序列?


输入格式:

第一行包含一个整数N。

第二行包含N个整数A1, A2, ..., AN。


输出格式:

一个整数代表答案。


样例输入:

5

11 121 22 12 2023


样例输出:

1


提示:

删除22,剩余11, 121, 12, 2023是接龙数列。


最道题咋一看是贪心  实则却是动态规划


正常情况下 两遍遍历这道题的时间复杂度应该是n方的 但是这样显然无法通过所有测试点  于是 我们使用动态规划的思想  来进行优化这道题


首先,我们定义一个二维数组 dp,其中 dp[i][j] 表示前 i 个接龙数组以数字 j 结尾的最少删除个数。


接下来,我们考虑状态转移方程。对于 dp[i][j],有两种情况:


  1. 如果前 i 个数的末尾数字是 left,末尾数字是 right,那么 dp[i][j] 可以通过删除第 i 个数字或者保留第 i 个数字得到。因此,我们可以得到状态转移方程:dp[i][j] = min(dp[i - 1][left], dp[i][j])。
  2. 要么把前 i 个数都删了,要么就是它本身,所以有 dp[i][j] = min(dp[i][j], i)。


最后,我们对于最后一位 dp[n-1][i],遍历 i 从 0 到 9,找到其中最小值,即为所求的答案。

int minDeletions = Integer.MAX_VALUE;
        for (int i = 0; i < 10; i++) {
            minDeletions = Math.min(minDeletions, dp[n - 1][i]);
        }

代码:

import java.util.Scanner;
 
public class Solution {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = scanner.nextInt();
        }
        int result = minDelete(n, arr);
        System.out.println(result);
    }
    
    public static int minDelete(int n, int[] arr) {
        // 定义dp数组
        int[][] dp = new int[n][10];
        for (int i = 0; i < n; i++) {
            dp[i][0] = i;
        }
        for (int j = 1; j <= 9; j++) {
            for (int i = 0; i < n; i++) {
                int left = arr[i] % 10; // 当前数的末尾数字
                int right = arr[i-1] / 10; // 上一个数的首位数字
                if (left == right) {
                    dp[i][j] = Math.min(dp[i-1][left], dp[i][j]);
                }
                dp[i][j] = Math.min(dp[i][j], i);
            }
        }
        // 计算最少删除的个数
        int ans = Integer.MAX_VALUE;
        for (int i = 0; i < n - 1; i++) {
            ans = Math.min(ans, dp[i][0]);
        }
        return ans;
    }
}

动态规划是一种常用的算法设计方法,用于解决一些特定类型的问题。它的核心思想是将问题分解为一系列子问题,并使用记忆化的方法来存储和更新状态信息,从而避免重复计算和时间复杂度的扩张。


我把动态规划的规律总结一下:


  1. 定义状态:首先,我们需要定义问题的状态。状态是描述问题某个特定状态的信息。状态可以是一个数字、一个数组、一个矩阵等,具体取决于问题的性质。状态的选择应该能够包含问题的所有可能情况。
  2. 构建状态转移方程:接下来,我们需要构建状态转移方程,描述如何从一个状态转移到另一个状态。状态转移方程是动态规划问题的核心。它是基于已知的子问题解来计算当前问题解的方法。
  3. 定义初始条件:在应用动态规划时,我们通常需要定义初始条件。初始条件是问题中最简单的情况,它们是解决问题的起点。初始条件的定义应该能够让状态转移方程正确运行。
  4. 计算最终结果:通过迭代计算状态转移方程,我们可以得到问题的最终结果。最终结果是根据问题的定义和要求得出的答案。


那么下一篇我们来看看 另一道蓝桥的真题 蜗牛


题目:


我的代码加理解:


public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
 
        int n = scan.nextInt();
        int[] x = new int[n + 1]; // 每根杆的横坐标
        int[] origin = new int[n + 1]; // 传送出发点的高度
        int[] dest = new int[n + 1]; // 传送到达点的高度
 
        // 读入每根杆的横坐标
        for (int i = 1; i <= n; i++) {
            x[i] = scan.nextInt();
        }
 
        // 读入传送出发点和到达点的高度
        for (int i = 1; i < n; i++) {
            origin[i] = scan.nextInt();
            dest[i + 1] = scan.nextInt();
        }
 
        // 动态规划dp数组
        double[][] dp = new double[n + 1][2];
        dp[1][0] = dp[1][1] = x[1]; // 第一根杆只能爬过去
 
        for (int i = 2; i <= n; i++) {
            // 计算去传送出发点的时间,可以从传送到达点出发,也可以从x轴出发
            
            //这里计算的是 如果选择了传送 上一个的到达点 到 本杆的传送点的时间 有两种情况 
            // ①到达点高需要往下走到传送点
            //②传送点高需要往上走到传送点
            double transferTime = origin[i - 1] > dest[i - 1] ? (origin[i - 1] - dest[i - 1]) / 0.7
                    : (dest[i - 1] - origin[i - 1]) / 1.3;
 
            
            // 这里假设上一杆到当前杆选择了传送的情况  就是两种情况 上上杆要么到达上一杆使用传送要么就走路 
            //①到达上一杆使用的是传送,他并不是从底部往上爬 而是在上上杆传送过来的,那么就要加上 上一到达点到本次传送点的距离(上一步计算的)
            //②到达上一杆使用的是走路 所以要加上从上一杆的底部爬到传送点的时间 也就是到达上一杆的最短时间加上向上爬的时间
            //取最小值
            transferTime = Math.min(dp[i - 1][1] + transferTime, dp[i - 1][0] + origin[i - 1] / 0.7);
 
            // 到达(X_i,0)所花时间 
            //0意味着他要选择走路 所以在这一条计算中 目标是走到该轮杆子的 底部
            //所以 有两种情况: 
            //①要么是 上一轮杆子选择传送到这个杆子的目标点(这里的时间就是transferTime) 加上从这个传送点上面下来的时间
            //②要么是 上一轮杆子选择走路到这个杆子,所以就是到达上一轮杆子的最短时间加上两杆之间的距离
            dp[i][0] = Math.min(transferTime + dest[i] / 1.3, dp[i - 1][0] + x[i] - x[i - 1]);
 
            // 到达第i根竹竿上的传送到达点所花时间
            //1意味着在这根杆子上他要选择传送 所以这一条计算的目标是他要走到本杆的传送点
            //那么就有两种情况:
            //①要么上一轮杆子选择了传送,所以就是刚才计算的到达下一杆的传送点的时间 
            //②要么上一轮杆子选择了走路,所以就是到达上一轮杆子的最短时间加上上爬到传送点的时间
            dp[i][1] = Math.min(transferTime, dp[i][0] + dest[i] / 0.7);
        }
 
        // 保留两位小数输出结果,即dp[n][0]
        System.out.println(String.format("%.2f", dp[n][0]));
    }
}


思路解析


首先,读入输入数据,包括竹竿的数量n,每根竹竿的横坐标x,以及传送出发点和到达点的高度origin和dest。


接下来,定义一个二维数组dp,用于存储状态转移的结果。dp[i][j]表示蜗牛到达第i根竹竿的状态,其中j=0表示蜗牛在竹竿上爬行,j=1表示蜗牛选择传送到达。


然后,对于第一根竹竿,蜗牛只能选择在x轴上爬行,所以dp[1][0]和dp[1][1]都等于x[1],即第一根竹竿的横坐标。


接下来,从第2根竹竿开始进行动态规划。对于每根竹竿,计算到达该竹竿的最短时间。


首先,计算蜗牛从上一根竹竿传送到达该竹竿的时间。这里有两种情况:如果传送点高度高于上一根竹竿的到达点高度,蜗牛需要爬下来,所以时间为(传送点高度-到达点高度)/0.7;如果传送点高度低于上一根竹竿的到达点高度,蜗牛需要爬上去,所以时间为(到达点高度-传送点高度)/1.3。取两种情况中的最小值。


然后,计算蜗牛到达该竹竿底部的时间。这里也有两种情况:如果上一根竹竿选择传送到达该竹竿的目标点,那么时间为传送时间加上从传送点爬下来的时间;如果上一根竹竿选择在x轴上爬行到达该竹竿,那么时间为上一根竹竿的最短时间加上两根竹竿之间的距离。取两种情况中的最小值。


最后,输出结果,保留两位小数。

相关文章
|
9天前
|
数据采集 DataWorks 搜索推荐
阿里云DataWorks深度评测:实战视角下的全方位解析
在数字化转型的大潮中,高效的数据处理与分析成为企业竞争的关键。本文深入评测阿里云DataWorks,从用户画像分析最佳实践、产品体验、与竞品对比及Data Studio公测体验等多角度,全面解析其功能优势与优化空间,为企业提供宝贵参考。
62 13
|
5天前
|
数据采集 存储 JavaScript
网页爬虫技术全解析:从基础到实战
在信息爆炸的时代,网页爬虫作为数据采集的重要工具,已成为数据科学家、研究人员和开发者不可或缺的技术。本文全面解析网页爬虫的基础概念、工作原理、技术栈与工具,以及实战案例,探讨其合法性与道德问题,分享爬虫设计与实现的详细步骤,介绍优化与维护的方法,应对反爬虫机制、动态内容加载等挑战,旨在帮助读者深入理解并合理运用网页爬虫技术。
|
12天前
|
存储 监控 调度
云服务器成本优化深度解析与实战案例
本文深入探讨了云服务器成本优化的策略与实践,涵盖基本原则、具体策略及案例分析。基本原则包括以实际需求为导向、动态调整资源、成本控制为核心。具体策略涉及选择合适计费模式、优化资源配置、存储与网络配置、实施资源监控与审计、应用性能优化、利用优惠政策及考虑多云策略。文章还通过电商、制造企业和初创团队的实际案例,展示了云服务器成本优化的有效性,最后展望了未来的发展趋势,包括智能化优化、多云管理和绿色节能。
|
18天前
|
编译器 PHP 开发者
PHP 8新特性解析与实战应用####
随着PHP 8的发布,这一经典编程语言迎来了诸多令人瞩目的新特性和性能优化。本文将深入探讨PHP 8中的几个关键新功能,包括命名参数、JIT编译器、新的字符串处理函数以及错误处理改进等。通过实际代码示例,展示如何在现有项目中有效利用这些新特性来提升代码的可读性、维护性和执行效率。无论你是PHP新手还是经验丰富的开发者,本文都将为你提供实用的技术洞察和最佳实践指导。 ####
27 1
|
25天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
27天前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
44 4
|
27天前
|
JSON API 开发者
微店(Weidian)商品详情API接口解析实战
微店(Weidian)是一个基于社交关系的电商平台,为商家提供了一整套的电商解决方案。微店API接口允许开发者通过编程方式访问和操作微店平台上的数据,从而可以创建自动化的工具、应用或集成服务。
|
29天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
67 2
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
75 0
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0

推荐镜像

更多