蓝桥:保险箱(Python,动态规划)

问题描述:

小蓝有一个保险箱,保险箱上共有 n 位数字。小蓝可以任意调整保险箱上的每个数字,每一次操作可以将其中一位增加 1 或减少 1。当某位原本为 9 或 0 时可能会向前(左边)进位/退位,当最高位(左边第一位)上的数字变化时向前的进位或退位忽略。

例如:
00000 的第 5 位减 1 变为 99999;
99999 的第 5 位减 1 变为 99998;
00000的第 4 位减 1 变为 99990;
97993 的第 4 位加 1 变为 98003;
99909 的第 3 位加 1 变为 00009。

保险箱上一开始有一个数字 x,小蓝希望把它变成 y,这样才能打开它,问小蓝最少需要操作的次数。

输入格式
输入的第一行包含一个整数 n。第二行包含一个 n 位整数 x。第三行包含一个 n 位整数 y。

输出格式
输出一行包含一个整数表示答案。

数据范围
对于 30% 的评测用例,1≤n≤300;
对于 60% 的评测用例,1≤n≤3000;
对于所有评测用例,1≤n≤105,x,y 中仅包含数字 0 至 9,可能有前导零。

输入样例:

5
12349
54321

输出样例:

11

思路:

又是一道省赛且没有完全AC的题,这道题刚开始压根没往动态规划方面想,结果是一个二维dp数组,其中一维表示3种状态,未进位,进位和退位状态。

下面进行分析:
分析题目发现每变动一位数字,只会影响左边的一个数。如果此时该位的数没有发生进位的话,左边的一个数就不会发生变动的;如果当前该位的数是通过加法进位的话,那么左边的一位数字就会加1;如果当前该位的数是通过减法进位的话,那么左边的一位数字就会减1。

所以发现当前一位数字是否发生变动是取决于后一位数字的变动,所以定义三种状态:

  1. 右边的数字没有发生进位
  2. 右边数字通过加法发生了进位
  3. 右边的数字通过减法发生了进位。

动规五部曲:

  1. 定义dp数组:

dp[i][0],dp[i][1],dp[i][2] 分别表示当前位没有发生进位、当前位通过加法发生进位和当前位通过减法发生进位需要操作的最少次数

  1. 递推公式:

接下来关键之处就是状态计算了,由于操作当前位并不影响右边的数,所以从右向左进行状态计算(用x表示第一个字符串当前位的数字,用y表示第二个字符串当前位的数字):

  • 当前位的数没有发生进位dp[i][0]

    1)如果右边的数操作完了之后当前位没有发生改变,那么:

    当前位不进位需要操作的次数即为:dp[i][0] = dp[i + 1][0] + abs(x - y)

    2)如果右边的数操作完了之后当前位增大了一位,那么:

    当前位不进位需要操作的次数即为:dp[i][0] = dp[i + 1][1] + abs(x + 1 - y)
    因为发生了进位,所以x要加1

    3)如果右边的数操作完了之后当前位减小了一位,那么:

    当前位不进位需要操作的次数即为:dp[i][0] = dp[i + 1][2] + abs(x - 1 - y)
    因为发生了退位,所以x要减1

    综上:dp[i][0] = min(dp[i + 1][0] + abs(x - y),dp[i + 1][1] + abs(x + 1 - y), dp[i + 1][2] + abs(x - 1 - y))

  • 当前位的数通过加法进位dp[i][1]

    1)如果右边的数操作完了之后当前位没有发生改变,那么:

    当前位通过加法进位需要操作的次数即为:dp[i][1] = dp[i + 1][0] + abs(10 - (x - y))
    如果x为2,y为8,则发生进位的情况是abs(10 -(2 - 8)) = 16,2进位到8需要加16

    2)如果右边的数操作完了之后当前位增大了一位,那么:

    当前位通过加法进位需要操作的次数即为:dp[i][1] = dp[i + 1][1] + abs(10 - (x + 1 - y))

    3)如果右边的数操作完了之后当前位减小了一位,那么:

    当前位通过加法进位需要操作的次数即为:dp[i][1] = dp[i + 1][2] + abs(10 - (x - 1 - y))

    综上:dp[i][1] = min(dp[i + 1][0] + abs(10 - (x - y)),dp[i + 1][1] + abs(10 - (x + 1 - y)), dp[i + 1][2] + abs(10 - (x - 1 - y)))

  • 当前位的数通过减法进位

    1)如果右边的数操作完了之后当前位没有发生改变,那么:

    当前位通过减法进位需要操作的次数即为:dp[i][2] = dp[i + 1][0] + abs(10 + (x - y))
    如果x为2,y为8,则发生退位的情况是abs(10 +(2 - 8)) = 4,2退位到8需要减4

    2)如果右边的数操作完了之后当前位增大了一位,那么:

    当前位通过减法进位需要操作的次数即为:dp[i][2] = dp[i + 1][1] + abs(10 + (x + 1 - y))

    3)如果右边的数操作完了之后当前位减小了一位,那么:

    当前位通过减法进位需要操作的次数即为:dp[i][2] = dp[i + 1][2] + abs(10 + (x - 1 - y))

    综上:dp[i][2] = min(dp[i + 1][0] + abs(10 + (x - y)),dp[i + 1][1] + abs(10 + (x + 1 - y)), dp[i + 1][2] + abs(10 + (x - 1 - y)))

  1. dp数组如何初始化:

因为递推公式是从右往左计算,所以

# 初始化最后一个位置的 dp 值
dp[n - 1][0] = abs(a[n - 1] - b[n - 1])  
dp[n - 1][1] = abs(10 - (a[n - 1] - b[n - 1])) 
dp[n - 1][2] = abs(10 + (a[n - 1] - b[n - 1]))  

  1. dp数组遍历顺序:

因为递推公式是从右往左计算,所以从右往左遍历

# 从倒数第二个位置开始向前遍历
for i in range(n - 2, -1, -1):
  1. 打印dp数组:

999 变到 321
上方表示状态
左侧表示数的索引值
如图:
在这里插入图片描述
最终答案为min(dp[0][0], dp[0][1], dp[0][2])

代码及详细注释:

# 读取输入的整数 n,表示数组的长度
n = int(input())
# 读取输入的两个整数列表 a 和 b,分别表示两个数组
a = list(map(int, input()))  # 将输入的字符串映射为整数列表
b = list(map(int, input()))  # 将输入的字符串映射为整数列表

# dp[i][j] 表示到达位置 i 时,使得 a[i] 的数字与 b[i] 的数字相等的最小操作次数,
# 其中 j 可能取值为 0、1、2,分别表示 a[i] - b[i] 等于 0、1、-1。
dp = [[0, 0, 0] for _ in range(n)]

# 初始化最后一个位置的 dp 值
dp[n - 1][0] = abs(a[n - 1] - b[n - 1])  
dp[n - 1][1] = abs(10 - (a[n - 1] - b[n - 1])) 
dp[n - 1][2] = abs(10 + (a[n - 1] - b[n - 1]))  

# 从倒数第二个位置开始向前遍历
for i in range(n - 2, -1, -1):
    x = a[i]
    y = b[i]

    # 计算当前位置的 dp 值
    dp[i][0] = dp[i + 1][0] + abs(x - y)  
    dp[i][0] = min(dp[i][0], dp[i + 1][1] + abs(x + 1 - y))  
    dp[i][0] = min(dp[i][0], dp[i + 1][2] + abs(x - 1 - y))  

    dp[i][1] = dp[i + 1][0] + abs(10 - (x - y))  
    dp[i][1] = min(dp[i][1], dp[i + 1][1] + abs(10 - (x + 1 - y)))  
    dp[i][1] = min(dp[i][1], dp[i + 1][2] + abs(10 - (x - 1 - y)))  

    dp[i][2] = dp[i + 1][0] + abs(10 + (x - y))  
    dp[i][2] = min(dp[i][2], dp[i + 1][1] + abs(10 + (x + 1 - y)))  
    dp[i][2] = min(dp[i][2], dp[i + 1][2] + abs(10 + (x - 1 - y)))  

# 输出最小操作次数
print(min(dp[0][0], dp[0][1], dp[0][2]))

总结:

省赛10分的题,结果写了半天ac了25%,拿了3分,之后看到类似的题分析到状态就要考虑是否能用dp来解决。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Rancher操作手册(v2.7.5-rc1)

1.登录 访问地址:10.66.55.132使用账号和密码登录。初始的页面是英文版本,可以点击左下方改为简体中文 登录成功后可以看到现有的集群。右上角可以进行新集群的创建和导入已有集群。点击箭头所指的蓝色集群名称可以进入集群。 2.集群仪表盘 进入到集…

Tuxera NTFS 2023安装使用教程 Tuxera NTFS破解版 Tuxera NTFS for Mac优惠

对于必须在Windows电脑和Mac电脑之间来回切换的Mac朋友来说,跨平台不兼容一直是一个巨大的障碍,尤其是当我们需要使用NTFS格式的硬盘在Windows和macOS之间共享文件时。因为Mac默认不支持写入NTFS磁盘。 为了解决这一问题,很多朋友会选择很便捷…

Simulink|局部遮荫下光伏组件多峰值PSO-MPPT控制

目录 主要内容 1.光伏电池工程数学模型的输出特性程序 2.普通扰动观察法进行MPPT 3.基于粒子群寻优的多峰输出特性 4.PSO_MPPT仿真模型 程序下载链接 主要内容 在实际的光伏发电系统中,由于环境多变等因素的影响,当局部出现被遮挡情况时光伏阵列的功率-电压(P-U)特…

SQLiteC/C++接口详细介绍之sqlite3类(十七)

返回目录:SQLite—免费开源数据库系列文章目录 上一篇:SQLiteC/C接口详细介绍之sqlite3类(十六) 下一篇: SQLiteC/C接口详细介绍之sqlite3类(十八) ​ 53.sqlite3_trace_v2 函数功能&#x…

【ArcGISPro】道路数据下载并使用

下载 下载链接: Geofabrik 下载服务器 这些数据通常 每天更新。 下载结果 arcmap用户下载工具 10.2:http://www.arcgis.com/home/item.html?id=16970017f81349548d0a9eead0ebba39 10.3:

最细节操作 Linux LVM 逻辑卷管理

Linux LVM(逻辑卷管理) 周末愉快,今天带大家实战一下LVM! 一、LVM理论 LVM,即Logical Volume Manager,逻辑卷管理器,是一种硬盘的虚拟化技术,可以允许用户的硬盘资源进行灵活的调整和动态管理…

Git版本管理--远程仓库

前言: 本文记录学习使用 Git 版本管理工具的学习笔记,通过阅读参考链接中的博文和实际操作,快速的上手使用 Git 工具。 本文参考了引用链接博文里的内容。 引用: 重学Git-Git远程仓库管理_git remote add origin-CSDN博客 Git学习笔记&am…

27-Java MVC 模式

Java空对象模式 实现范例 MVC模式代表 Model-View-Controller(模型-视图-控制器) 模式MVC模式用于应用程序的分层开发 Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO 它也可以带有逻辑,在数据变化时更新控制…

【系统架构设计师】系统工程与信息系统基础 01

系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 文章目录 系列文章目录 前言 一、系统工程 ★ 二、信息系统生命周期 ★ 信息系统建设原则 三、信息系统开发方法 ★★ 四、信息系统的分类 ★★★ 1.业务处理系统【TPS】 2.管理信息系统【MIS】 3.决策支持系统…

由浅到深认识C语言(11):结构体

该文章Github地址:https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn…

提升物流效率,快递平台实战总结与分享

随着电商行业的蓬勃发展,物流配送服务变得愈发重要。快递平台作为连接电商企业和消费者的桥梁,扮演着至关重要的角色。本篇博客将分享快递平台实战经验,总结关键要点,帮助物流从业者提升物流效率、优化服务质量。 ### 快递平台实…

小米Mini路由器刷Openwrt

前言 在我们使用路由器,会有把想要的路由器修改为openwrt后使用,今天这里分享了一下小米mini,但是总体小米路由器基本都是一样的操作,先进行回退某个可以支持ssh的版本,再使用注入命令,最后烧录breed和ope…

微信小程序开发系列(三十四)·自定义组件的创建、注册以及使用(数据和方法事件的使用)

目录 1. 分类和简介 2. 公共组件 2.1 创建 2.2 注册 2.3 使用 3. 页面组件 3.1 创建 3.2 注册 3.3 使用 4. 组件的数据和方法的使用 4.1 组件数据的修改 4.2 方法事件的使用 1. 分类和简介 小程序目前已经支持组件化开发,可以将页面中的功能…

深度解析:如何运用山海鲸可视化软件制作高效销售数据看板

在数字化时代,数据可视化已经成为企业决策和运营的重要工具。作为一名长期使用山海鲸可视化软件的资深用户,我深知其在制作销售数据可视化看板方面的优势。今天,我想分享一些我在使用山海鲸可视化软件制作销售数据可视化看板过程中的经验和感…

面向控制台编程?Java的GUI开发

记得之前刚开始学习Java,按部就班去阅读《Java核心技术》这本书的时候,总是听别人提起,java swing那一章不用看了。然后直到对着控制台编程了半年,回来捡起了Swing图形界面,跟着网上搞了坦克大战的游戏,总觉…

抖去推无人直播+矩阵托管+AI文案撰写一体化工具如何开发搭建

一、 开发和搭建抖去推无人直播矩阵托管AI文案撰写一体化工具需要以下步骤: 确定功能需求:确定抖去推无人直播、矩阵托管和AI文案撰写的具体功能需求,如直播推流、直播管理、托管服务、AI文案生成等。 技术选型:选择适合开发该工…

Spring Boot 中的 Sleuth 详解

Spring Boot 中的 Sleuth 是一个用于分布式追踪的库,它可以帮助你追踪和理解分布式系统中的请求如何跨越多个服务和网络调用。通过使用 Sleuth,你可以收集关于请求路径、延迟、异常等的信息,从而更容易地诊断问题并进行性能优化。 一、下面是…

ArcGIS分享图层数据的最佳方法

在工作中,经常需要将图层数据分享给其他人。 如下图所示,需要分享的是【CJDCQ】和【GHDLTB】,图层带有符号系统: 一、分享gdb数据库及lyr文件 分享数据自然要找到源数据: 但是,gdb数据是不带符号系统的&a…

软考高级:软件工程瀑布模型概念和例题

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

Android 开发 地图 polygon 显示信息

问题 Android 开发 地图 polygon 显示信息 详细问题 笔者进行Android项目开发,接入高德地图绘制区域后,需要在指定区域(位置)内显示文本信息,如何实现 实现效果 解决方案 代码 import com.amap.api.maps.model.T…