黑白按键-第13届蓝桥杯选拔赛Python真题精选

[导读]:超平老师的Scratch蓝桥杯真题解读系列在推出之后,受到了广大老师和家长的好评,非常感谢各位的认可和厚爱。作为回馈,超平老师计划推出《Python蓝桥杯真题解析100讲》,这是解读系列的第67讲。

黑白按键,本题是2021年8月14日举办的第13届蓝桥杯青少组Python编程选拔赛真题。题目要求编程计算按几次按键可以使初始图变为最终图,如果不能实现则输出0。

先来看看题目的要求吧。

一.题目说明

编程实现:

有一组黑白按键,每按下其中一个按键,其相邻的按键和它本身都会变成相反的颜色(黑色变白色,白色变为黑色)。

如果按下的按键非最左边和最右边按键,则其本身和左右相邻的两个按键变相反颜色;

如果按下最左边按键,则其本身和右边相邻的一个按键变相反颜色;

如果按下最右的按键,则其本身和左边相邻的一个按键变相反颜色。

图片

给出一张“初始图”和一张“最终图”。通过按下按键,使“初始图”变为“最终图”,求最少需要按几次可以完成。

如:初始图为黑、白、黑3个按键(状态表示:010),最终图为白、白、黑3个按键(状态表示:110)。

首先按下2号按键,3个按键颜色变为白、黑、白(状态标识:101),然后按下3号按键,3个按键颜色变为白、白、黑(状态标识:110),故使初始图”变为“最终图”最少需要按2次。

如下图:

图片

输入描述:

第一行输入一个由“0"和“1"组成的字符串,字符串长度为n(1< n < 26),表示游戏初始图状态,“0"表示黑色按键,“1"表示白色按键

第二行输入一个由“0"和“1"组成的字符串,字符串长度为n(1 < n < 26),表示游戏最终图状态,“0"表示黑色按键,“1"表示白色按键

输出描述:

输出一个整数,如果通过按键不能使初始图变为最终图,则输出“0,否则输出最少需要按几次按键可以使初始图变为最终图

样例输入:

010

110

样例输出:

2

二.思路分析

这是一道算法题,考查的算法有模拟和回溯,涉及的知识点包括循环、条件、列表和函数和排列组合等。

本题描述的问题,稍微有些复杂,可以将其拆分为两个部分:

1). 翻转按键

2). 计算最小次数

先来讨论翻转按键,对于任何给定的初始状态,按下其中的某个按键,都会得到一个新的状态,可以称之为中间状态。

比如,对于初始状态110,按下2号按键,变成中间状态001,再按下3号键,中间状态变为了110,此时中间状态刚好等于最终状态,所以翻转次数为2。

这其实就是一个简单的模拟算法,为了方便,可以定义一个函数,用于翻转按键颜色。

需要给定两个参数,一个是初始状态color,一个是按键编号index,如下:

def flip(color, index):  pass

根据题目描述的规则,需要考虑index的位置,有三种情形:

  • 如果index = 0,翻转自己和右邻居的按键;

  • 如果index = n - 1,翻转自己和左邻居的按键;

  • 如果index不在两端,则翻转自己和左右邻居的按键;

接下来,我们再分析如何计算最小次数,还是从实际例子出发,假定初始状态为010,最终状态为110。

这里一共有3个按键,根据排列组合知识,一共有6种不同的顺序,可以分别计算按每种顺序翻转需要的次数,如下:

图片

通过这个表格可以发现,在6种不同的顺序中,有4种顺序是无法翻转到最终状态状态的,这里使用-1表示,有两种顺序只需要翻转两次就可以变为最终状态。

再来看一个案例,假定初始状态为01,最终状态为11,它只有两种不同的顺序,分别翻转并计算次数如下:

图片

可以发现,把所有的排列都尝试一遍,无法从状态01翻转到状态10。

通过这两个案例,相信你心里有了个大概的思路。无非就是把所有的排列都计算一遍,看看哪一种排列顺序使用的次数最少,同时还需要考虑无法实现的情况。

所以,从本质上讲,这是一个排列组合问题,在Python编程中,通常有如下两种实现方式:

  • 使用permutations()函数

  • 使用回溯算法

其中,permutations()函数是itertools库提供的函数,使用起来非常方便,但随着n的增加,其时间复杂度越来越高,本题中的n最大为25,极有可能出现超时情况。

相对来说,回溯算法效率要高一些,同时还可以使用剪枝操作避免不必要的循环。比如,在本题中,可以使用一个变量保存最小翻转次数,一旦发现在翻转时已经超过最小翻转次数,可以立即结束回溯。

关于回溯算法的基本用法,可以参考《N皇后问题-第12届蓝桥杯省赛Python真题精选》这篇教程,这里就不再单独介绍了。

思路有了,接下来,我们就进入具体的编程实现环节。

三.编程实现

根据上面的思路分析,我们分三步来编写程序:

  • 定义翻转函数

  • 定义回溯函数

  • 计算最小次数

1. 定义翻转函数

根据前面的思路分析,定义翻转函数如下:

图片

代码不过,说明4点:

1). 参数color表示当前按键的状态,类型是字符串,index表示当前按键的编号,从0开始;

2). 为方便处理,将color转成列表,翻转完成后,再转成字符串,作为函数的返回值;

3). 如果index > 0,说明有左边的按键,将左边按键翻转,如果index < len(color) -1,说明有右边的按键,将右边按键翻转,对于当前位置index来说,直接翻转;

4). 在翻转按键的时候,使用了if...else的条件赋值语句,代码更加简洁,如果不熟悉,也可以改成if...else条件语句;

2. 定义回溯函数

回溯函数的写法基本上是固定的,编写代码如下:

图片

代码不多,但是理解起来还是有些难度的,说明3点:

1). 函数参数有两个,color表示当前按键状态,flips表示翻转次数,每调用一次backtrack()函数,flips就增加1次;

2). 代码中用到了两个全局变量,第一个是min_flips,它表示最小翻转次数,需要在函数中进行修改,因此要加上global关键字进行声明,final表示按键的最终状态;

3). 结束回溯有3种不同的情况,第一种是color = final,就是已经翻转到最终状态了,此时要更新min_flips,第二种是flips > min_flips,说明这不是我们要的结果,那就直接结束,这就是所谓的剪枝,第三种是全部回溯完毕,说明无解,直接返回,注意三者的顺序,不能随意更改;

3. 计算最小次数

最后就是主程序了,获取用户的输入并调用函数,代码如下:

图片

代码比较简单,说明两点:

1). 变量min_flips表示最小翻转次数,初始化的时候要设置为最大值,这里的float('inf')表示无穷大;

2). 调用backtrack()函数之后,如果不能翻转成功,那么min_flips是没有更新的,其值仍然是float('inf'),此时直接输出0。

至此,整个程序就全部完成了,你可以输入各种不同的按键状态来测试效果啦。

四.总结与思考

本题代码在30行左右,涉及到的知识点包括:

  • 循环语句;

  • 条件语句,尤其是带条件单独赋值语句;

  • 列表的使用;

  • 列表和字符串的转换;

  • 自定义函数;

  • 模拟算法;

  • 回溯算法;

本题代码不少,难度较大,这里的关键点是对两种算法的灵活运用,一是模拟算法,二是回溯算法。

所谓模拟算法,就是利⽤计算机对⼀个过程进⾏模拟,通过改变数学模型的各种参数,观察这些参数的变化所引起的过程状态的变化,并从中得出答案。

比如,使用计算机编程模拟抛硬币得到正反面概率的情况,模拟报数游戏等。

一般来说,不是用经典算法解出来的算法都可以称为模拟算法,本题的翻转按键也是一个典型的模拟算法。

而回溯算法,其本质仍然是枚举算法,但它做了一些改进和优化,它跟暴力搜索最大的不同在于,在回溯算法里,会对每一步探测到的情况进行评估,如果当前的情况已经无法满足要求,那么就没有必要继续进行下去,也就是说,它可以帮助我们避免走很多的弯路。

超平老师给你留一道思考题,如果不使用回溯算法,直接使用permutations()函数,如何获取最小翻转次数呢?

你还有什么好的想法和创意吗,也非常欢迎和超平老师分享探讨。

如果你觉得文章对你有帮助,别忘了点赞和转发,予人玫瑰,手有余香😄

需要源码的,可以移步至“超平的编程课”gzh。

  • 9
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值