【Python】AI 程序设计练习题题解


说明:由于课程使用的编程语言为 Python,所以题解使用 Python 编程实现,题解仅供参考,如有更好的解题思路欢迎评论讨论。

A. 逻辑斯蒂克函数

题目链接:逻辑斯蒂克函数
解答一:根据数学表达式写出 Python 表达式即可。

print("{:.1f}".format(1 / (1 + 2.71828 ** (-float(input())))))

解答二:使用 math 库的 exp 函数实现。

from math import exp
print("%.1f" % (1 / (1 + exp(-float(input())))))

B. 圆的周长

题目链接:圆的周长
分析:根据题目给出的面积公式和周长公式可以得到周长与面积的关系为 C = 2 π S C = 2\sqrt{\pi S} C=2πS 。注意题目要求保留六位小数,所以 π \pi π 的精度尽量高一些,否则可能 WA。
解答一:根据推到出来的公式写出 Python 表达式即可。

print("{:.6f}".format(2 * (3.14159265358979 * float(input())) ** 0.5))

解答二:也可使用 math 库自带的常量 pi 和 sqrt 函数。

import math
print("%.6f" % (2 * math.sqrt(math.pi * float(input()))))

C. 单词的个数

题目链接:单词的个数
解答:由于匹配时不区分大小写,所以可以把文章和查询单词都转换为小写(或都转换为大写),又因为要求全词匹配,所以直接按照空格切分即可。

print(input().lower().split().count(input().lower()))

D. 反转字符串

题目链接:反转字符串
解答一:按照题目要求定义大小写转换函数 f,然后把反转后的字符存在列表里,使用 reverse 方法反转后输出即可。其中,Python 自带的 ord 函数可以将字符转化成 ASCII 码,chr 函数可以将 ASCII 码转化成字符。

def f(c):
	asc = ord(c)
	a_low, a_up = ord('a'), ord('A')
	if a_low <= asc <= ord('z'):
		return chr(asc - a_low + a_up)
	return chr(asc - a_up + a_low)

string = [f(char) for char in input()]
string.reverse()
print(''.join(string))

解答二:也可用字典完成大小写转换,并使用 [::-1] 进行反转。

f = dict(zip(map(chr, range(ord('a'), ord('z'))), map(chr, range(ord('A'), ord('Z')))))
f.update(dict(zip(map(chr, range(ord('A'), ord('Z'))), map(chr, range(ord('a'), ord('z'))))))
print(''.join([f[c] for c in input()[::-1]]))

E. 反转数字

题目链接:反转数字
解答:思路与反转字符串类似,不需要大小写转换,但是要去掉输入字符串最后面的 0 0 0。需要注意, 0 0 0 的反转还是 0 0 0,需要单独考虑。

num = input().rstrip('0')
print(num[::-1] if len(num) else 0)

F. 修正线性单元

题目链接:修正线性单元
解答:判断输入的数是否是正数即可,如果是正数就直接输出,如果不是正数就输出 0 0 0。对于最后一组测试数据,由于 n n n 很大,所以不能把输入全存起来,否则会 MLE,另外,由于 ∣ x i ∣ |x_i| xi 很大,用 int 函数转成整数的时候需要很长时间,会 TLE,所以不能转成整数,直接判断字符串是不是以负号开头即可。

for _ in range(int(input())):
    x = input()
    print(0 if x.startswith('-') else x)

G. 打分

题目链接:打分
解答:使用 max 函数和 min 函数找出最高分和最低分,对其他数求平均即可,最后用 math 库的 ceil 函数向上取整。

from math import ceil
n = int(input())
scores = [int(input()) for _ in range(n)]
print(ceil((sum(scores) - max(scores) - min(scores)) / (n - 2)))

H. 临时抱佛脚

题目链接:临时抱佛脚
解答:根据分数对所有人进行排序即可。

data = []
for _ in range(int(input())):
    name, score = input().split()
    data.append([name, int(score)])
for output in sorted(data, key=lambda x: x[1], reverse=True):
    print(output[0])

I. 谁得了二等奖

题目链接:谁得了二等奖
解答:先使用 max 函数求出最高分,然后遍历一遍成绩找出第二高分即可。

index, second = None, -1
data = [tuple(map(int, input().split())) for _ in range(int(input()))]
best = max(data, key=lambda x: x[1])[1]
for num, score in data:
	if second < score < best:
		index, second = num, score
print(index)

J. 简易计算器

题目链接:简易计算器
解答:题目看似复杂,其实就是输入 Python 表达式,输出表达式的值,把幂运算符号 ^ 替换成 ** 再调用 eval 函数即可解决。

[print(int(eval(input().replace('^', "**")))) for _ in range(int(input()))]

K. 第几天

题目链接:第几天
解答:通过字符串切片的方式获得年份、月份和日期,然后根据每个月的天数判断这天是第几天,注意考虑闰年的情况。

s = input()
y, m, d = int(s[:4]), int(s[4:6]), int(s[6:])
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if (y % 4 == 0 and y % 100 != 0) or y % 400 == 0:
    days[1] += 1
print(sum(days[:m - 1]) + d)

L. 解方程

题目链接:解方程
解答:根据费马大定理,方程 x n + y n = z n x^n + y^n = z^n xn+yn=zn n > 2 n > 2 n>2 时没有正整数解,所以当且仅当 n = 1 n = 1 n=1 时有一个方程有正整数解,其他情况都是只有前两个方程有正整数解。对于最后一组样例,输入的 n n n 可能很大,使用 int 转换很慢,会 TLE,所以直接使用字符串判断 n n n 的值是否为 1 1 1

print(1 if input() == '1' else 2)

M. 高空抛物

题目链接:高空抛物
解答:等比数列求和问题。第一次落地时经过的距离为 h h h,之后每次弹起的高度是首项为 h 2 \dfrac{h}{2} 2h、公比为 1 2 \dfrac{1}{2} 21 的等比数列,因为弹起后再落地是一上一下的过程,所以每次弹起后落地经过的距离是首项为 h h h、公比为 1 2 \dfrac{1}{2} 21 的等比数列,第 n n n 次落地需要求这个数列的前 n − 1 n - 1 n1 项和,得到 2 h ( 1 − 2 1 − n ) 2h\left (1 - 2^{1-n} \right) 2h(121n),最后加上一开始落地经过的距离 h h h

h = int(input())
n = int(input())
p = 1 << (n - 1)
print(h + 2 * h * (p - 1) // p)

N. 海鲜大餐

题目链接:海鲜大餐
解答:这道题本质上是列表去重问题,但是可以换个思路,因为海鲜的种类数最多为 5 × 1 0 5 5 \times 10^5 5×105,所以我们用一个长度为 5 × 1 0 5 5 \times 10^5 5×105 的列表存储每一个种类的海鲜是否被小未买了,在循环的过程中,每来一个海鲜,就判断一下这个种类的海鲜是不是已经有了,如果没有就把它标记成有,否则就把答案加一,这样代码不仅速度快 而且内存占用也很少

ans = 0
seafoods = [False] * (5 * 10 ** 5 + 1)
for _ in range(int(input())):
	index = int(input())
	if seafoods[index]:
		ans += 1
	else:
		seafoods[index] = True
print(ans)

O. 最小公倍数

题目链接:最小公倍数
解答:先使用 小学学过的 辗转相除法求出 a a a b b b 的最大公因数,再根据 小学学过的 最小公倍数与最大公因数的关系( a a a b b b 的最大公因数与最小公倍数的乘积等于 a b ab ab)算出答案。

a, b = int(input()), int(input())
y, x = sorted([a, b])
while y:
	x, y = y, x % y
print(a * b // x)

P. 找书

题目链接:找书
解答:由于书的编号是有序的,所以我们使用二分查找的方法。具体做法是,给定 n n n 本书和要查找的编号 t t t,先去找第 n 2 \dfrac{n}{2} 2n 本书的编号是不是 t t t,如果是 t t t,就找到了,如果比 t t t 小,就继续在第 1 1 1 本书到第 n 2 \dfrac{n}{2} 2n 本书里接着用这个方法找,如果比 t t t 大,就在第 n 2 \dfrac{n}{2} 2n 本书到第 n n n 本书里接着用这个方法找,直到找到编号为 t t t 的书,或者要找的书的范围为空则代表没有编号为 t t t 的书。用代码实现就是一个递归函数。

def find(books, num):
    n = len(books)
    if n == 0:
        return False
    if n == 1:
        return num == books[0]
    mid = n >> 1
    if num == books[mid]:
        return True
    if num < books[mid]:
        return find(books[:mid], num)
    else:
        return find(books[mid + 1:], num)

numbers = tuple(map(int, input().split()))
while True:
    query = int(input())
    if query == 0:
        break
    print('Y' if find(numbers, query) else 'N')

Q. 排兵布阵

题目链接:排兵布阵
解答:这是一个搜索问题,需要找到所有可能的出战情况,最后统计数量。我们用一个集合来存储所有人的全排列,排列情况用元组表示,由于集合自带去重功能,所以最后答案就是这个集合的长度。那么如何得到所有人的全排列呢, m m m 个 for 循环就可以了, 可以写一个递归函数 f ( x , y ) f(x, y) f(x,y),其中 x x x 是一个元组,表示当前遍历的结果, x x x 的长度就表示当前在第几个 for 循环里, y y y 也是一个元组,表示哪些人被遍历过了。每次遍历的时候,通过一个 for 循环把不在 y y y 里的人 z z z 加入到 x x x 中,然后递归调用 f ( x + z , y + z ) f(x + z, y + z) f(x+z,y+z) 就能实现 m m m 个 for 循环的效果。

s = set()
n, m = map(int, input().split())
array = list(map(int, input().split()))

def dfs(now, indexes):
    if len(now) == m:
        s.add(now)
        return
    for i, a in enumerate(array):
        if i in indexes:
            continue
        dfs(now + (a,), indexes + (i,))

dfs((), ())
print(len(s))

R. 括号的合法性

题目链接:括号的合法性
解答一:考虑用一个列表来辅助我们进行判断,从左到右遍历字符串,如果遇到了左括号,就把它加入到列表中,如果遇到了右括号,就看看列表是不是空(因为列表只存储左括号),如果不空,就拿出一个左括号与之匹配,如果空,就说明这个右括号没有左括号与之匹配。最后,如果列表空了,说明所有的左括号都有右括号与之对应,否则说明有多余的左括号。

stack = []
for char in input():
	if char == '(':
		stack.append(char)
	elif len(stack):
		stack.pop()
	else:
		print('N')
		exit()
print('N' if len(stack) else 'Y')

解答二:使用万能的 eval 函数。因为括号在 Python 中可以表示元组、运算符和函数调用,所以如果括号是合法的,那么 eval 后要么不报错,要么报元组不能被调用的错(TypeError),否则就会报不符合语法规范的错(SyntaxError),因此使用 eval 函数加异常处理语句即可解决。

try:
    eval(input())
    print('Y')
except TypeError:
    print('Y')
except SyntaxError:
    print('N')

S. 第一名

题目链接:第一名
解答:其实就是求最大值,直接使用 max 函数就行了。可是这里比较两个成绩谁高谁低的逻辑很复杂,用 max 函数只能判断总分或者其中某一科的分数呀。没事,因为我们可以 运算符重载!定义一个 Score 类,重载这个类的大于小于等运算符方法即可。

N, M = map(int, input().split())

class Score:
    def __init__(self, name: int, scores: list):
        self.id = name
        self.score = scores
        self.total = sum(scores)
    
    def __eq__(self, other):
        if self.total != other.total:
            return False
        return self.score == other.score
    
    def __gt__(self, other):
        delta = self.total - other.total
        if delta > 0:
            return True
        if delta < 0:
            return False
        for m in range(M):
            delta = self.score[m] - other.score[m]
            if delta > 0:
                return True
            if delta < 0:
                return False
        return False
    
    def __lt__(self, other):
        delta = self.total - other.total
        if delta < 0:
            return True
        if delta > 0:
            return False
        for m in range(M):
            delta = self.score[m] - other.score[m]
            if delta < 0:
                return True
            if delta > 0:
                return False
        return False
    
    def __ge__(self, other):
        return self > other or self == other
    
    def __le__(self, other):
        return self < other or self == other

print(max([Score(i, list(map(int, input().split()))) for i in range(N)]).id + 1)

T. 独木桥

题目链接:独木桥
解答:这道题看似是一个很复杂的过程,但是可以这么想,当两个人相遇时,他们分别掉头走相当于两个人直接越过了对方。所以,这道题的答案就是所有向北走的人到桥最北边的距离最大值与所有向南走的人到桥最南边的距离的最大值的最大值。

L = int(input())
s, n = [0], [L]
for _ in range(int(input())):
    direction, distance = input().split()
    (s if len(direction) == 2 else n).append(int(distance))
print(max(max(s), L - min(n)))

U. 选礼物

题目链接:选礼物
解答一:先翻译一下题目,给定一个长度为 n n n 的数组,问其中连续的几个数的和最大可以是多少?用两次循环解决即可得到 100 100 100 分,外层循环遍历起始值 L L L,内层循环遍历终止值 R R R,求出最大值即可。

n = int(input())
ans = -float("inf")
a = [int(input()) for _ in range(n)]
for i in range(n):
    s = 0
    for j in range(i, n):
        s += a[j]
        ans = max(ans, s)
print(ans)

解答二:事实上,一次循环就可以解决这个问题。用变量 s s s 记录当前数组的和的最大值,变量 m m m 记录答案。初始时,变量 s = 0 s = 0 s=0,变量 m = − ∞ m = -\infty m=。每次遍历时,求当前数组的和,更新 m ← max ⁡ { m , s } m \gets \max \lbrace m, s \rbrace mmax{ m,s},如果当前数组的和 s < 0 s < 0 s<0,说明前面这些数起到了副作用,直接舍弃这些数,重置 s = 0 s = 0 s=0。这种方法可得满分。

s, m = 0, -float("inf")
for _ in range(int(input())):
    s += int(input())
    m = max(m, s)
    if s < 0:
        s = 0
print(m)

V. 旗鼓相当

题目链接:旗鼓相当
解答一:用两层 for 循环两两比较没两位同学刷题数量的差值即可获得 100 100 100 分。

n = int(input())
a = [int(input()) for _ in range(n)]
ans = 10 ** 6
for i in range(n):
    for j in range(i + 1, n):
        ans = min(ans, abs(a[j] - a[i]))
print(ans)

解答二:两层 for 循环太慢了,考虑对每位同学的刷题数量进行排序,那么最接近的两位同学肯定出现在排序后的列表的相邻位置,可得满分。

n = int(input())
a = [int(input()) for _ in range(n)]
ans = 10 ** 6
a.sort()
for i in range(1, n):
    ans = min(ans, a[i] - a[i - 1])
print(ans)

W. 爬楼梯

题目链接:爬楼梯
解答一:简单起见,我们先考虑 s = 2 s = 2 s=2,即小未每步最多只能走两个台阶的情况:

  • 如果 n = 1 n = 1 n=1,显然只有 1 1 1 种方式;
  • 如果 n = 2 n = 2 n=2,那么有 2 2 2 种方式,即直接走两个台阶和分两步走一个台阶
  • 如果 n ≥ 3 n \ge 3 n3,那么小未可以从第 n − 1 n - 1 n1 个台阶处走 1 1 1 个台阶直接到达,也可以从第 n − 2 n - 2 n2 个台阶处一步走 2 2 2 个台阶直接到达,因此,如果设到达第 n n n 个台阶的方式总数为 f ( n ) f(n) f(n),则有 f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n-1) + f(n - 2) f(n)=f(n1)+f(n2)

下面,我们考虑 s = 3 s = 3 s=3 的情况:

  • 如果 n = 1 n = 1 n=1,显然只有 1 1 1 种方式;
  • 如果 n = 2 n = 2 n=2,那么有 2 2 2 种方式,即直接走两个台阶和分两步走一个台阶;
  • 如果 n = 3 n = 3 n=3,那么有 4 4 4 种方式,即直接走三个台阶,或者先走一个台阶再走两个台阶,或者先走两个台阶再走一个台阶,或者分三步每步都只走一个台阶;
  • 如果 n ≥ 4 n \ge 4 n4,那么小未可以从第 n − 1 n - 1 n1 个台阶处走 1 1 1 个台阶直接到达,也可以从第 n − 2 n - 2 n2 个台阶处一步走 2 2 2 个台阶直接到达,还可以从第 n − 3 n - 3 n3 个台阶处一步走 3 3 3 个台阶直接到达,因此,如果设到达第 n n n 个台阶的方式总数为 f ( n ) f(n) f(n),则有 f ( n ) = f ( n − 1 ) + f ( n − 2 ) + f ( n − 3 ) f(n) = f(n-1) + f(n - 2) + f(n - 3) f(n)=f(n1)+f(n2)+f(n3)

我们再来考虑 s = 1 s = 1 s=1 的情况:

  • 如果 n = 1 n = 1 n=1,显然只有 1 1 1 种方式,即 f ( 1 ) = 1 f(1) = 1 f(1)=1
  • 如果 n ≥ 2 n \ge 2 n2,也只有 1 1 1 种方式(因为一次只能上一个楼梯),也就是 f ( n ) = 1 f(n) = 1 f(n)=1,那其实就是 f ( n ) = f ( n − 1 ) f(n) = f(n - 1) f(n)=f(n1)

你发现了什么?
是的,如果我们用 f ( n ) f(n) f(n) 表示到达第 n n n 个台阶的方式总数,那么当 n ≥ s + 1 n \ge s + 1 ns+1 时,有 f ( n ) = ∑ i = n − s n − 1 f ( n − i ) f(n) = \sum_{i=n-s}^{n-1}f(n-i) f(n)=i=nsn1f(ni) 这不就是类似于斐波那契数列的东西吗。最后还需要注意一下,我们只要存这个数列的后两位就行了,不然会 MLE。下面的代码可以得到 100 100 100 分。

s, n = int(input()), int(input())
f = [1, 2, 4][:s]
if n <= s:
	print(f[n - 1] % 100)
else:
	for _ in range(n - s):
	    f.append(sum(f[-s:]) % 100)
	print(f[-1])

解答二:如果要得满分,需要考虑 s ≤ 10 s \le 10 s10 的情况,其实就是要算出 f f f 的前 s s s 项, n ≤ s n \le s ns 时,有 f ( n ) = 1 + ∑ i = 1 n − 1 f ( i ) f(n) = 1+\sum_{i=1}^{n-1}f(i) f(n)=1+i=1n1f(i) f ( 1 ) = 1 f(1) = 1 f(1)=1 f ( 2 ) = 2 f(2) = 2 f(2)=2,所以用数学归纳法不难证明 f ( n ) = 1 + ∑ i = 1 n − 1 2 i − 1 = 2 n − 1 f(n) = 1 + \sum_{i=1}^{n-1}2^{i-1} = 2^{n-1} f(n)=1+i=1n12i1=2n1这样就可以把 f f f 初始化成长度为 s s s 的列表了。另外,由于 n n n 特别大,for 循环到底是肯定会 TLE 的,因此需要找到数列 f f f 末尾两位数的循环周期。

s = int(input())
f = [(2 ** p) % 100 for p in range(s)]
while True:
    f.append(sum(f[-s:]) % 100)
    if f[:s] == f[-s:]:
        f = f[:-s]
        break
print(f[int(input()) % len(f) - 1])

X. 平方和

题目链接:平方和
解答一:得出数列 { a n } \lbrace a_n \rbrace { an} 的递推公式 a n = 10 a n − 1 + 8 a_n = 10a_{n-1} + 8 an=10an1+8,用一个 for 循环求和即可获得 100 100 100 分。

a, s = 8, 0
for _ in range(int(input())):
    s += a ** 2
    a = 10 * a + 8
print(s)

解答二:对于最后两个测试数据, n n n 比较大,for 循环求和会 TLE,因此需要做一些 简单的 数学推导。
{ b n } \lbrace b_n \rbrace { bn} 是各位数都为 9 9 9 的数列,即 a n = 8 9 b n a_n = \dfrac{8}{9}b_n an=98bn,于是 S n = ∑ i = 1 n a i 2 = 8 2 9 2 ∑ i = 1 n b i 2 = 64 81 ∑ i = 1 n ( 1 0 i − 1 ) 2 = 64 81 ∑ i = 1 n ( 1 0 2 i − 2 × 1 0 i + 1 ) \begin{alignat}{2} S_n & = \sum_{i=1}^{n}a_i^2 \nonumber \\ & = \dfrac{8^2}{9^2}\sum_{i=1}^{n}b_i^2 \nonumber \\ & = \dfrac{64}{81}\sum_{i=1}^{n}(10^i - 1)^2 \nonumber \\ & = \dfrac{64}{81}\sum_{i=1}^{n}\left(10^{2i} - 2 \times 10^i + 1\right) \nonumber \end{alignat} Sn=i=1nai2=9282i=1nbi2=8164i=1n(10i1)2=8164i=1n(102i2×10i+1) 其中: ∑ i = 1 n 1 0 2 i = 1010 ⋯ 10 ⏟ n 个 10 0 \sum_{i=1}^{n}10^{2i} = \underbrace{1010 \cdots 10}_{n个10}0 i=1n102i=n10 1010100 ∑ i = 1 n 2 × 1 0 i = 22 ⋯ 2 ⏟ n 个 2 0 \sum_{i=1}^{n}2 \times 10^i = \underbrace{22 \cdots 2}_{n个2}0 i=1n2×10i=n2 2220 ∑ i = 1 n 1 = n \sum_{i=1}^{n}1 = n i=1n1=n 于是可以直接求得 S n S_n Sn

n = int(input())
print(((int("10" * n) - int('2' * n)) * 10 + n) * 64 // 81)

Y. 玩游戏

题目链接:玩游戏
解答一:先翻译一下题目,给定一个长度为 n n n 的数组 h h h,从 h h h 中拿出一些数,保证这些数单调不减,问最多能拿出几个数,最终答案是数的个数减一(因为问的是移动次数,不是柱子个数)。我们假设 g ( i ) g(i) g(i) 表示以 h i h_i hi 结尾的单调不减序列的最长长度,则不难写出递推式 g ( i + 1 ) = max ⁡ { 1 , max ⁡ j < i , h j ≤ h i { g ( j ) + 1 } } g(i + 1) = \max \lbrace 1, \underset{j < i, h_j \le h_i}{\max} \lbrace g(j) + 1 \rbrace \rbrace g(i+1)=max{ 1,j<i,hjhimax{ g(j)+1}} 最终答案就是 max ⁡ 1 ≤ i ≤ n { g ( i ) } \underset{1 \le i \le n}{\max} \lbrace g(i) \rbrace 1inmax{ g(i)},可得 100 100 100 分。

n = int(input())
g = [1] * n
h = [int(input()) for _ in range(n)]
for i in range(1, n):
    g[i] = max([g[i]] + [g[j] + 1 for j in range(i) if h[j] <= h[i]])
print(max(g) - 1)

解答二:设 f ( x ) f(x) f(x) 表示从 h h h 中拿出的长度为 x x x 的序列的最后一个数的最小值,初始时令 x = 0 x = 0 x=0,然后开始遍历,如果 h i ≥ f ( x ) h_i \ge f(x) hif(x),则更新 f ( x + 1 ) = h i f(x + 1) = h_i f(x+1)=hi,并把 x x x 的值加一,如果 h i < f ( x ) h_i < f(x) hi<f(x),就从后往前更新 f ( x ) , f ( x − 1 ) , ⋯   , f ( 1 ) f(x),f(x-1),\cdots,f(1) f(x),f(x1),,f(1),看看有没有 j j j 满足 f ( j − 1 ) ≤ h i < f ( j ) f(j - 1) \le h_i < f(j) f(j1)hi<f(j) 的,如果有,说明长度为 j j j 的序列的最后一个数有更小的值 h i h_i hi。这种方法可得满分。

f = [0]
for _ in range(int(input())):
	h = int(input())
    if h >= f[-1]:
        f.append(h)
    else:
        for i in range(len(f) - 1, 0, -1):
            if f[i - 1] <= h < f[i]:
                f[i] = h
print(len(f) - 2)

猜你喜欢

转载自blog.csdn.net/qq_56131580/article/details/134173760
今日推荐