[导读]:超平老师计划推出《全国青少年信息素养大赛Python编程真题解析》50讲,这是超平老师解读Python编程挑战赛真题系列的第18讲。

全国青少年信息素养大赛(原全国青少年电子信息智能创新大赛)是“世界机器人大会青少年机器人设计与信息素养大赛”赛事之一,由中国电子学会主办,包含很多赛项,大赛自2013年举办,已连续成功举办八届,已正式入围“2022-2025学年面向中小学生的全国性竞赛活动名单”。

大赛旨在激发广大青少年的科学兴趣和想象力,培养钻研探究、创新创造的科学精神和实践能力,促进青少年科技创新活动的广泛开展,发现和培养一批具有科研潜质和创新精神的青少年科技创新后备人才。

大赛主要竞赛类别包括电子科技、智能机器人、软件编程三类,全国青少年Python编程挑战赛就属于其中的软件编程类。

一.赛事说明

2023年(第9届)Python挑战赛赛程分为初赛、复赛和总决赛三个阶段。初赛是资格赛,复赛是地方选拔赛,总决赛是全国各地选拔的精英汇聚在一起进行PK。

本届Python挑战赛是在线上举行,参赛选手登录大赛官网在指定页面完成答题并提交答案。评定成绩的依据是同时考虑得分和用时两个方面,首先是得分高者名次靠前,如果得分一样,则用时少者名次靠前。

2023年全国青少年Python编程挑战赛华南赛区(海南)复赛于2023年7月8日正式举行。采取的仍然是在线考试,一共有6道题,全是编程题,考试时间是90分钟。

今天超平老师分享的是第6题,卡牌游戏。

二.题目说明

题目描述:

有 n 张卡牌。第 i 张卡牌的位置是 position[i]。

我们需要把所有卡牌移到同一个位置。在一步中,我们可以将第 i 张卡牌的位置从 position[i]改变为:

position[i] + 2 或 position[i] – 2,此时的代价 cost = 0

position[i] + 1 或 position[i] – 1,此时的代价 cost = 1

现给出所有卡牌的位置,请你编程输出将所有卡牌移动到同一位置上所需要的最小代价。

时间限制:1000ms

内存限制:256MB

输入描述:

输入一行正整数,每个数之间用空格间隔

输出描述:

输出将所有卡牌移动到同一位置上所需要的最小代价。

样例 1:

输入:

1 2 3

输出:

1

三.思路分析

卡牌游戏是我们生活中大家喜闻乐见的一种娱乐方式,相信大家都不会陌生。

首先,我们要正确理解输入一行正整数的意思及卡牌的摆放方式,如果输入的是1、2、3,其摆放效果如下:

如果输入的是1、2、2,其摆放效果如下:

其次,我们要彻底读懂题目的意思。对于处在任意位置上的卡牌,有如下两种移动方式:

  • 方式1:往前或往后移动2格,代价为0

  • 方式2:往前或往后移动1格,代价为1

最终目标是花费最小的代价来将所有卡牌移动到同一位置,这里的同一位置可以是1 ~ n之间的任意位置。

很显然,我们可以使用贪心策略来解决这个问题,那什么是贪心策略呢?

先来看看经典名著《算法导论》是如何解释的?

贪心算法,又称贪婪算法,一个贪心算法总是做出当前最好的选择,也就是说,它期望通过局部最优选择得到全局最优的解决方案。

举一个简单的例子,有一堆钞票,你可以拿走10张,如果想拿走最大数额的钱,那么要怎么拿?

一定是每次拿面额最大的钞票,最终结果就是拿走数额最大的钱。每次拿面额最大的钞票就是局部最优,最后可以拿走最大数额的钱就是全局最优。

回到本题,为了确保代价最小,对于每一次的移动选择,如果能选择方式1(cost = 0),就绝对不选择方式2(cost = 1)。

因此我们可以通过方式1先将尽量多的卡牌移动到一起,在剩下的卡牌无法再通过方式1来移动到同一位置的时候,使用最少次数的方式2来移动。

例如,对于样例1 2 3,先利用方式1,将position[3]位置上的K移动到position[1],此时的代价为0。

现在剩下的两堆卡牌无法再通过方式1移动到一块,因此只能够使用方式2来移动。这样的移动选择有两种:

  • 使用方式1,将position[1]上的两张卡牌J和K移动到position[2],其总代价为2

  • 使用方式2,将position[2]上的卡牌Q移动到position[1],其总代价为1

很显然,使用后者可以让代价更小,也就是说,将所有卡牌移到第1个位置,其总代价为1。当然,这个位置并不是唯一的,如果都移到第3个位置,其总代价也为1。

对于3张卡牌的情形,相信你已经彻底弄明白了。如果有更多的卡牌,又该怎么移动呢?

根据上面的贪心策略,我们要尽量先使用方式1移动卡牌,那么方式1最多能移动多少张卡牌到同一个位置上呢?

答案是能够将所有奇数位置的卡牌移动到同一个位置,记作A,再将所有偶数位置的卡牌移动到同一位置,记作B。

并且A和B可以是任意两个相邻的位置,这就意味着两者的差为1。之所以要相邻,是因为相邻的位置移动的次数最少。

为方便说明,我们统一将奇数位置的卡牌移动到position[1],将偶数位置的卡牌移动到postion[2]。

例如输入的正整数为 1 2 3 4 5 6 7 8 9,即position = [1, 2, 3, 4, 5, 6, 7, 8, 9],如图所示:

我们先通过方式1将其中所有奇数位置的卡牌(1,3,5,7,9)移动到位置position[1],如图:

再将所有偶数位置的卡牌(2,4,6,8)移动到位置position[2],如图:

如此一来,position[1]上共有5张卡牌,position[2]上共有4张卡牌。然后利用方式2将postion[2]上的4张卡牌全部移动到position[1]上,总代价就是4。

至此,我们可以总结规律如下:

所有奇数位置的卡牌都可以通过方式1移动到位置postion[1],所有偶数位置的卡牌都可以通过方式1移动到位置position[2],这个过程的总代价为0。

然后使用方式2将postion[1]和postion[2]两者中卡牌数量较少的那一堆移动到另一堆即可。

由此,我们可以得出最终算法如下:

1). 分别统计postion数组中奇数位置偶数位置的卡牌数量,分别用odd和even来表示;

2). odd和even的较小者就是最小代价。

接下来,我们进入具体的编程实现环节。

四.编程实现

根据上面的思路分析,使用贪心算法,编写代码如下:

简单说明两点:

1). 对于用户输入的一行正整数,我们先将其按空格拆分,得到一个字符数字列表,类似于[‘1’, ‘2’, ‘3’]这种,再通过int函数将其转成整数,这里使用了列表推导式的编程技巧;

2). 比较两个数字的大小,可以直接使用python自带的max和min函数。

五.总结与思考

本题代码不多,考查的知识点主要包括:

  • 处理输入数据;

  • 列表推导式;

  • for…in循环;

  • if…else双分支语句;

  • 取模运算符;

  • 贪心算法思想;

从代码的的角度来看,本题属于简单题目,但实际上,很多同学还没有走到这一步,在寻求解题思路的途中就已经迷路了。

本题的难点在于对问题的分析,其中,贪心算法是指导思想,拆分问题是方法。我们要坚信,一个复杂的问题,总是可以拆分成多个简单的问题。把每个简单的小问题解决了,那么整个问题也就解决了,这就是人们常说的结构化思维,也是编程学科提倡的计算思维。

通过这道题,相信你也感觉到了,学习少儿编程绝不是简单地编写几个程序,而是要运用逻辑思维来分析问题,借助数学思维来构建模型和算法,最后才是使用编程语言来实现,它是多个学科的完美融合。

学习编程能够培养孩子发现问题、分析问题并解决问题的综合能力,这种能力是未来社会最需要的一种能力。

你还有什么巧妙的解决方案吗,欢迎和超平老师交流。

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

查看更多教程,请移步至“超平的编程课”gzh。