Python解题 - CSDN周赛第20期 - 逆波兰 / 后缀表达式

不知不觉已经参加了19场比赛了,由于参赛次数多,排名竟然稀里糊涂地上升到第一,受宠若惊,赶紧截图保存纪念一下。

正好又赶上元旦,新的2023年,希望CSDN的周赛能越办越好,尽量少出bug,也希望自己能够保持学习持续进步,在python算法领域以及大众编程普及方面能够取得一点点成绩。


第一题:非降序数组

写一个函数,传入两个非降序的整数数组(A, B),将 A, B 合并成一个非降序数组 C,返回 C(不要使用内置 sort 函数)。

分析

题目说明了不准使用内置 sort 函数,不然一句代码就可以解题了。但是话又说回来,官方真的会手动检查吗?

此题还是经典的归并排序(印象里考了好几次了),依次比较两个数组元素的大小,添加进新的数组,同时移动下标的位置即可。

参考代码

n, m = map(int, input().strip().split())
num1 = [int(item) for item in input().strip().split()]
num2 = [int(item) for item in input().strip().split()]
result = []
i = j = 0
while i<n and j<m:
    if num1[i]<num2[j]:
        result.append(num1[i])
        i += 1
    else:
        result.append(num2[j])
        j += 1
result.extend(num1[i:])
result.extend(num2[j:])
print(*result)

第二题:吃!吃!吃!

已知n个整数。 每3个数算一个周期。 不足一个周期补0。在周期的第一个位置的数的权值和等于a1+a[1+3]+… … 第二个位置的数的权值和等于a2+a[2+3]+… … 求三个数的最大权值。 如果最大权值为第一个数,输出’J’;最大权值为第二个数,输出’H’;最大权值为第三个数,输出’B’。

分析

题目描述得有点费劲,看了半天才懂,就是没看出和标题的“吃吃吃”有什么关系。所谓的权值其实就是 n 个整数每隔3个数加在一起的总和,而这种“每隔几个数如何如何”的做法,使用python列表的切片操作可以说是信手拈来,而且都不用去关心够不够3个数或一个周期、需不需要补0,因为切片操作如果得到的是空列表,使用加总计算会自动得到0。

最后我们得到三个数,还要找出他们的最大值,然后根据最大值所在位置的不同,输出不同的字母。因为只有三个数,怎么做都行,问哥使用了字典,当然使用循环找出最大值也没差。

参考代码

n = int(input().strip())
arr = [int(item) for item in input().strip().split()]
result = []
for i in range(3):
    result.append(sum(arr[i:n:3]))
r = {0:"J",1:"H",2:"B"}
print(r[result.index(max(result))])

第三题:计算逆波兰表达式的结果

逆波兰记法中,操作符置于操作数的后面。例如表达“三加四”时,写作“3 4 +”,而不是“3 + 4”。如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的“3 - 4 + 5”在逆波兰记法中写作“3 4 - 5 +”:先3减去4,再加上5。使用逆波兰记法的一个好处是不需要使用括号。例如中缀记法中“3 - 4 * 5”与“(3 - 4)*5”不相同,但后缀记法中前者写做“3 4 5 * -”,无歧义地表示“3 (4 5 *) -”;后者写做“3 4 - 5 *”。

示例:

示例一示例二
输入

3 4 5 * -

123 23 12 + -

输出17-88

分析

逆波兰表达式(Reverse Polish Notation, RPN),也叫后缀表达式,是纪念波兰逻辑学家Jan Lukasiewicz的成就,不知道是不是因为他的名字太难记了,世人使用他的国籍而不是名字来命名。该表达式最大的作用就是让计算机更容易操作人类的数学表达式,也不需使用括号来标记运算的优先级。

而更为我们所熟知的却是“后缀表达式”这个叫法,因为我们人类使用的数学表达式一般称为中缀表达式,顾名思义,“中缀”就是指运算符号在两个数的中间,而“后缀”则在两个数的后面。而中缀转后缀的方法,相信是所有学过数据结构的同学都练习过的,这也是栈操作的经典方式。

而本题的后缀表达式实际上有个“小错误”,从示例二就可以看出来,其计算结果是负数,说明它是使用栈顶第一个元素作为双目运算式左边的数字,第二个元素作为右边的数字,这和传统的后缀表达式是正好相反的,和题目中的描述也有所不同(所以我按照这个逻辑自己编了示例一)。不知道是不是考题故意为之,也许是为了考察大家是否真正懂得了后缀表达式计算的原理吧。

计算过程其实并不复杂:

  1. 依次读入后缀表达式,如果遇到数字,则数字进栈;
  2. 如果遇到运算符,则将栈顶的两个数字出栈,使用该运算符进行计算,将计算结果重新入栈;
  3. 如此往复,直到读完后缀表达式。

下图显示了示例二的计算过程:

参考代码

arr = input().strip().split()
n = len(arr)
result = []
for i in range(n):
    if arr[i].isdigit():
        result.append(arr[i])
    else:
        a = result.pop()
        b = result.pop()
        r = str(int(eval(a+arr[i]+b)))
        result.append(r)
print(r)

由于本题没有限定双目计算符的范围(除了加减乘除,还有可能是乘方、整除等) ,所以问哥也没有将字符串转成整数型,然后再去判断运算符的种类,从而进行不同的运算。取而代之地,直接借助python的eval函数,可以将字符串的算术表达式计算出结果。这样也许有投机取巧之嫌,但既然python提供了这么方便的函数,完全没必要再去重复造轮子了,而且本题的重点是掌握逆波兰表达式的原理即可。


第四题:会议安排

开会了!作为一个集体,开会的时候桌子当然是需要和老大相邻的!(老大可能坐在桌子上边) 小艺被分配到排桌椅的活,可是小艺的力气都用在吃上了,怎么可能搬动这些桌椅呢。 她决定用现有的布局当作是会议座位安排。每个桌子分配一个人。互相连接相同字符表示一个桌子,如UVV表示2张桌子,UVU则表示3张桌子。小艺想知道选择某个桌子之后老大身边能围多少人?

示例:

示例
输入

3 4 R

G.B.

BRRA

TTT.

输出

4

分析

本题不难,但是太繁也太麻烦。不知道是不是问哥对题目理解得不够深入,反正我是使用穷举法找出的答案,就是找出和“老板”所在的“桌子”(字符串)相邻的上下左右四个方向有多少连续的、不同的字符串。所以,首先,是要找到“老板”所在的“桌子”的上下左右四个边界(当然,本题也有个简化的前提:这个“桌子”是矩形的,如果是不规则的形状就更加麻烦了),然后根据这个边界,去搜寻四边的字符串有哪些字符是连续的、相同的(小数点“.”代表空地,除外)。

比如示例中的老板所在的桌子是R,如下图所示,上边相邻的是B,下边相邻的是T,左边相邻的是B,右边相邻的是A,总共四个位置,答案就是4。

代码实现也不难,但问哥总觉得应该有更优化更优雅的方法,放在这里权当抛砖引玉了。 

参考代码

n, m, c = input().strip().split()
n = int(n)
m = int(m)
vector = []
for i in range(n):
    vector.append(input().strip())
res = 0
top = left = bottom = right = -1
for i in range(n):
    for j in range(m):
        if top < 0 and vector[i][j] == c:
            top, left = i, j # “老板”所在“桌子”的左上角
        if bottom < 0 and vector[n-i-1][m-j-1] == c:
            bottom, right = n-i-1, m-j-1 # “老板”所在“桌子”的右下角
    if top >= 0 and bottom >= 0: break # 找到“桌子”边界,退出搜索
a = b = "."
for i in range(left, right+1): 
    if top > 0 and vector[top-1][i] != a: # 搜寻上方相邻的字符串
        a = vector[top-1][i]
        if a != ".": res += 1
    if bottom < n-1 and vector[bottom+1][i] != b: # 搜寻下方相邻的字符串
        b = vector[bottom+1][i]
        if b != ".": res += 1
a = b = "."
for i in range(top, bottom+1): 
    if left > 0 and vector[i][left-1] != a: # 搜寻左边相邻的字符串
        a = vector[i][left-1]
        if a != ".": res += 1
    if right < m-1 and vector[i][right+1] != b: # 搜寻右边相邻的字符串
        b = vector[i][right+1]
        if b != ".": res += 1
print(res)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我问哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值