天梯赛讲解

7-1 N个数求和

题目要求:

本题的要求很简单,就是求N个数字的和。麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和也必须是有理数的形式。

输入格式:

输入第一行给出一个正整数N(≤100)。随后一行按格式a1/b1 a2/b2 ...给出N个有理数。题目保证所有分子和分母都在长整型范围内。另外,负数的符号一定出现在分子前面。

输出格式:

输出上述数字和的最简形式 —— 即将结果写成整数部分 分数部分,其中分数部分写成分子/分母,要求分子小于分母,且它们没有公因子。如果结果的整数部分为0,则只输出分数部分。

输入样例

 5
 2/5 4/15 1/30 -2/60 8/3

输出样例:

 3 1/3

由输出样例可知:可能输出:1.整数+分数 2.只有整数(包括0) 3.只有分数

思路:1.存储数据 2.通分(分母公倍数) 3.分子求和 4.输出

存储数据:

 num = input()
 list_1 = input().split()  # 存储数据
 list_2 = []
 for i in list_1:
     list_2.append(i.split('/'))
 for i in list_2:
     i[0] = int(i[0])
     i[1] = int(i[1])  # 将字符串转化为整数

存储结果:

 [[2, 5], [4, 15], [1, 30], [-2, 60], [8, 3]]  # 以2/5 4/15 1/30 -2/60 8/3为例

寻找分母公倍数(没有必要找最小):

 x = 1
 for i,j in list_2:
     x *= j

通分:

 list_3 = []
 for i,j in list_2:
     list_3.append([i*x//j, x])

分子相加:

 y = 0
 for i, j in list_3:
     y += i

输出:

  • 若分子为0,则结果为0

 if y == 0:
     print(0)
  • 分子不为0(要保证输出的分数为最简真分数)

    • 输出整数

    • 输出分数

    • 输出既有整数,也有分数

 else:
     m = x  # 欧几里得算法(辗转相除法)求最大公约数
     n = abs(y)  
     while m % n != 0: 
         m, n = n, m % n  # 可以拆开写,但要引入t
     z = n  # 求出最大公约数
     fen_zi = y // z
     fen_mu = x // z
     if fen_zi >= 0:  # 为正数
         re_1 = fen_zi // fen_mu
         re_2 = fen_zi - re_1 * fen_mu
     else:  # 为负数
         re_1 = abs(fen_zi) // fen_mu  # 避免负数整除出现,直接用绝对值
         re_2 = abs(fen_zi) - re_1 * fen_mu
         re_1 = -re_1
         if re_1 == 0:
             re_2 = -re_2
     if re_2 == 0:  # 只有整数
         print(re_1)
     elif re_1 == 0:  #只有分数
         print(f"{re_2}/{fen_mu}")
     else:  # 既有整数也有分数
         print(f"{re_1} {re_2}/{fen_mu}")

求最大公约数的五种算法

【算法】辗转相除法求最大公约数

python负数取余


7-2 比较大小

题目要求:

本题要求将输入的任意3个整数从小到大输出。

输入格式:

输入在一行中给出3个整数,其间以空格分隔。

输出格式:

在一行中将3个整数从小到大输出,其间以“->”相连。

输入格式:

 4 2 8

输出格式:

 2->4->8

思路:1.存储数据 2.用sorted函数排序 3.用join函数进行拼接

 x = input()
 num = [int(i) for i in x.split()]  # 采用列表推导式将字符串转化为整数便于排序
 y = sorted(num)
 re = "->".join(map(str, y))  # 先用map函数将整数转化为字符,再用join函数连接每个数字(也可以用for循环)
 print(re)

7-3 A-B

题目要求:

本题要求你计算AB。不过麻烦的是,AB都是字符串 —— 即从字符串A中把字符串B所包含的字符全删掉,剩下的字符组成的就是字符串AB

输入格式:

输入在2行中先后给出字符串AB。两字符串的长度都不超过104,并且保证每个字符串都是由可见的ASCII码和空白字符组成,最后以换行符结束。

输出格式:

在一行中打印出AB的结果字符串。

输入样例:

 I love GPLT!  It's a fun game!
 aeiou

输出样例:

 I lv GPLT!  It's  fn gm!

思路:从A中去除所有在B中出现的字符,并把他们用join拼接

 A = input()
 B = input()
 re = ''.join([i for i in A if i not in B])  # 使用列表推导式,选出只在A中不在B中的字符并用join函数拼接
 print(re)

7-4 计算指数

题目要求:

真的没骗你,这道才是简单题 —— 对任意给定的不超过10的正整数n,要求你输出2n。不难吧?

输入格式:

输入在一行中给出一个不超过10的正整数n

输出格式:

在一行中按照格式 2n = 计算结果输出2n的值。

输入样例:

 5

输出样例:

 2^5 = 32

直接输出即可:

 x = int(input())
 print(f"2^{x} = {2**x}")

7-5 计算阶乘和

题目要求:

对于给定的正整数N,需要你计算 S=1!+2!+3!+...+N!。

输入格式:

输入在一行中给出一个不超过10的正整数N

输出格式:

在一行中输出S的值。

输入样例:

 3

输出样例:

 9

思路:定义一个函数求阶乘(可用递归),求和即可

 def jie_cheng(n):
     if n == 0 or n == 1:  # 注意0和1的阶乘都为1
         return 1
     else:
         return n * jie_cheng(n - 1)
 ​
 ​
 x = int(input())
 re = 0
 for i in range(1,x+1):  # 求和
     re += jie_cheng(i)
 print(re)

7-6 简单题

题目要求:你只需要在一行中输出事实:This is a simple problem. 就可以了。

 print("This is a simple problem.")

7-7 跟奥巴马一起画方块

题目要求:

美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统。2014年底,为庆祝“计算机科学教育周”正式启动,奥巴马编写了很简单的计算机代码:在屏幕上画一个正方形。现在你也跟他一起画吧!

输入格式:

输入在一行中给出正方形边长N(3≤N≤21)和组成正方形边的某种字符C,间隔一个空格。

输出格式:

输出由给定字符C画出的正方形。但是注意到行间距比列间距大,所以为了让结果看上去更像正方形,我们输出的行数实际上是列数的50%(四舍五入取整)。

输入样例:

 10 a

输出样例:

 aaaaaaaaaa
 aaaaaaaaaa
 aaaaaaaaaa
 aaaaaaaaaa
 aaaaaaaaaa

思路:这道题我PE了,不知道哪里有问题,最后下来又试了试发现是round函数的原因

原代码:

 N, C = input().split()  # 接收行数和字符
 N = int(N)
 row = round(N / 2)  # 用round进行四舍五入
 for i in range(row):
     print(C * N)

下来改完后:

 N, C = input().split()
 N = int(N)
 row = int(N / 2.0 + 0.5)
 for i in range(row):
     print(C * N)

和我有一样错误的同学推荐去看一下round函数的详解:

详解python中的round()函数


7-8 查验身份证

题目要求:

一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:

首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:

 Z:0 1 2 3 4 5 6 7 8 9 10
 M:1 0 X 9 8 7 6 5 4 3 2

现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。

输入格式:

输入第一行给出正整数N(≤100)是输入的身份证号码的个数。随后N行,每行给出1个18位身份证号码。

输出格式:

按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出All passed

输入样例:

 4
 320124198808240056
 12010X198901011234
 110108196711301866
 37070419881216001X

输出样例:

 12010X198901011234
 110108196711301866
 37070419881216001X

思路:定义一个函数检查算出的身份证校验码是否与原校验码一致

校验函数:

 def examine(ID):
     weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]  # 权重
     match = {0: 1, 1: 0, 2: 'X', 3: 9, 4: 8, 5: 7, 6: 6, 7: 5, 8: 4, 9: 3, 10: 2}  # 用字典表示对应关系
     if not ID[:-1].isdigit():  # 用isdigit函数判断身份证前17位是否全为为数字
         return True  # True表示为身份证不合法
     we_sum = sum([int(i) * j for i,j in zip(ID[:-1], weights)])  # 用列表推导式+zip函数计算加权和
     z = we_sum % 11  # 求得z
     valide_code = match[z]  # 得到校验码
     if str(valide_code) != ID[-1]:  # 比对校验码
         return True
     else:
         return False

输入输出:

 n = int(input())
 list_1 = []
 for i in range(n):
     ID = input()
     if examine(ID):
         list_1.append(ID)
 if list_1:  # list_1中若有身份证号
     for i in list_1:
         print(i)
 else:
     print("All passed")

7-9 集合相似度

题目要求:

给定两个整数集合,它们的相似度定义为:Nc/Nt×100%。其中Nc是两个集合都有的不相等整数的个数,Nt是两个集合一共有的不相等整数的个数。你的任务就是计算任意一对给定集合的相似度。

输入格式:

输入第一行给出一个正整数N(≤50),是集合的个数。随后N行,每行对应一个集合。每个集合首先给出一个正整数M(≤104),是集合中元素的个数;然后跟M个[0,109]区间内的整数。

之后一行给出一个正整数K(≤2000),随后K行,每行对应一对需要计算相似度的集合的编号(集合从1到N编号)。数字间以空格分隔。

输出格式:

对每一对需要计算的集合,在一行中输出它们的相似度,为保留小数点后2位的百分比数字。

输入样例:

 3
 3 99 87 101
 4 87 101 5 87
 7 99 101 18 5 135 18 99
 2
 1 2
 1 3

输出样例:

 50.00%
 33.33%

思路:主要是要设计一个计算集合相似度的函数

 def similarity(set_1,set_2):
     """
     计算相似度
     用intersection函数取两个集合的交集
     用union函数取两个集合的并集
     """
     return len(set_1.intersection(set_2)) / len(set_1.union(set_2)) * 100  # *100方便化为百分数

输入:

 N = int(input())
 set_0 = [set(map(int, input().split()[1:])) for i in range(N)]  #用列表推导式+map函数将集合中的所有数字转化为整数

等价于:

 set_0 = []
 for i in range(N):
   x = input().split()
   set_ = set(map(int, x[1:]))  # 还是采用map函数,将M后的数字化为整数
   set_0.append(set_)

存储结果:

 [{99, 101, 87}, {5, 101, 87}, {99, 5, 101, 135, 18}]  # 以给出的测试用例为例

输出:

 K = int(input())
 for i in range(K):
     s = list(map(int, input().split()))
     set_1 = set_0[s[0] - 1]  # 第s[0]个集合
     set_2 = set_0[s[1] - 1]  # 第s[1]个集合
     sim = similarity(set_1,set_2)  计算相似度
     print(f"{sim:.2f}%")

7-10 树的遍历

题目要求:

给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

 7
 2 3 1 5 7 6 4  # 后续遍历
 1 2 3 4 5 6 7  # 中序遍历

输出样例:

 4 1 6 3 5 7 2  # 层次遍历

思路:根据二叉树的后序遍历和中序遍历结果构建二叉树,再进行层次遍历然后输出

先来复习一下前中后序遍历:

再来复习一下层次遍历:

根据二叉树后续遍历和中序遍历结果构建二叉树(主要运用递归思想):

 def build_tree(po, io):  # po为PostOrder缩写,表示后序遍历列表,io为InOrder缩写,表示中序遍历列表
     if not po or not io:  # 若po或者io有一个为空列表,二叉树为空
         return None
     root_val = po[-1]  # 从后序遍历列表中取出根节点的值 root_val
     root_idx = io.index(root_val)  # 用index函数查找根节点在中序遍历列表中的索引root_idx
     left = build_tree(po[:root_idx], io[:root_idx])  # 递归调用 build_tree 函数构建左子树
     right = build_tree(po[root_idx:-1], io[root_idx + 1:])  # 递归调用 build_tree 函数构建右子树
     return [root_val, left, right]  # 返回一个包含根节点值、左子树、右子树的列表

根据二叉树进行层次遍历并直接输出(利用队列先进先出的特点):

 def order_(tree):  # tree表示二叉树的根节点
     result = []
     if not tree:  # 如果输入的二叉树根节点为空,直接返回一个空列表
         return result
     queue = [tree]  # 使用队列实现层序遍历,将根节点入队
     while queue:
         node = queue.pop(0)  # 在每次迭代中,将队列的第一个节点出队,将其值添加到结果列表中
         result.append(node[0])
         if node[1]:  # 如果节点有左子树,将左子树入队
             queue.append(node[1])
         if node[2]:  # 如果节点有右子树,将右子树入队
             queue.append(node[2])
     print(" ".join(map(str, result)))  # 重复以上步骤直到队列为空,得到层序遍历的结果列表,并直接输出

为什么采用队列:在层次遍历过程中,首先访问根节点,然后按照从左到右的顺序访问每一层的节点,使用队列可以保证每次取出的节点都是当前层次中最左边的节点,然后依次将该节点的子节点加入队列,以便在下一轮循环中按照顺序访问。

输入:

 N = int(input())
 postorder = list(map(int, input().split()))
 inorder = list(map(int, input().split()))

构建二叉树:

 root = build_tree(postorder, inorder)

层次遍历并输出:

 result = order_(root)

7-11 家庭房产

题目要求:

给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。

输入格式:

输入第一行给出一个正整数N(≤1000),随后N行,每行按下列格式给出一个人的房产:

 编号 父 母 k 孩子1 ... 孩子k 房产套数 总面积

其中编号是每个人独有的一个4位数的编号;分别是该编号对应的这个人的父母的编号(如果已经过世,则显示-1);k(0≤k≤5)是该人的子女的个数;孩子i是其子女的编号。

输出格式:

首先在第一行输出家庭个数(所有有亲属关系的人都属于同一个家庭)。随后按下列格式输出每个家庭的信息:

 家庭成员的最小编号 家庭人口数 人均房产套数 人均房产面积

其中人均值要求保留小数点后3位。家庭信息首先按人均面积降序输出,若有并列,则按成员编号的升序输出。

输入样例:

 10
 6666 5551 5552 1 7777 1 100
 1234 5678 9012 1 0002 2 300
 8888 -1 -1 0 1 1000
 2468 0001 0004 1 2222 1 500
 7777 6666 -1 0 2 300
 3721 -1 -1 1 2333 2 150
 9012 -1 -1 3 1236 1235 1234 1 100
 1235 5678 9012 0 1 50
 2222 1236 2468 2 6661 6662 1 300
 2333 -1 3721 3 6661 6662 6663 1 100

输出样例:

 3
 8888 1 1.000 1000.000
 0001 15 0.600 100.000
 5551 4 0.750 100.000

思路:根据关系分类存储相关信息,类似家庭关系树的思想,用字典把相关的人都合并到一起

构造一个函数便于找到根节点:

 def find_root(key, dic1):
     if key in dic1:  # 如果 key 在 dic1 中存在,且 dic1[key] 等于 key,表示找到了根节点,直接返回 key
         if dic1[key] == key:
             return key
         return find_root(dic1[key], dic1)
     else:  # 递归调用 find_root 函数,传入 dic1[key] 作为新的 key
         return key

变量初始化:

  • dic1: 保存每个成员的根节点信息。

  • dic2: 保存每个根节点对应的成员列表。

  • dic3: 记录房产套数和房产面积。

  • dic4: 最终输出的结果,包含每个家族的统计信息。

存储家族信息并构建家族树:

 n = int(input()) 
 for i in range(n):  # 使用for循环读取每个成员的信息
     data = input().split()
     dic3[data[0]] = [int(data[-2]), int(data[-1])] #记录房产套数和房产面积
      family_members = []  # 记录家庭成员
     for j in data[0:3]:
         if j != "-1":  # 记录父母
             family_members.append(j)
     for z in range(int(data[3])):  # 记录子女
         family_members.append(data[4 + z])
     if data[1] == "-1" and data[2] == "-1":  # 考虑父母双亡
         flag = data[0]  # 更新根节点
     elif data[1] == "-1" and data[2] != "-1":  # 父死,母没死
         flag = data[2]
     else:  # 父没死,母死和父母健在
         flag = data[1]
     l.append(family_members)  # 用l: 保存每个家族的成员列表
     for member in family_members:
         dic1[member] = find_root(flag, dic1)  #  保存每个成员的根节点信息

如下例所示:

 {'6666': '5551', '5551': '5551', '5552': '5551', '7777': '5551'}  # 家族每个成员的根节点信息

l存储结果:

 [['6666', '5551', '5552', '7777'], ['1234', '5678', '9012', '0002'], ['8888'], ['2468', '0001', '0004', '2222'], ['7777', '6666'], ['3721', '2333'], ['9012', '1236', '1235', '1234'], ['1235', '5678', '9012'], ['2222', '1236', '2468', '6661', '6662'], ['2333', '3721', '6661', '6662', '6663']]

构建家族关系树:

 for family in l:  # 遍历每个家族,找到每个成员的根节点,并将家族成员关系整合到 dic1 中
     root = find_root(family[0], dic1)
     for key in family:
         if dic1[key] != root:
             dic1[dic1[key]] = find_root(root, dic1)
         dic1[key] = root

dic1存储结果:

 {'6666': '5551', '5551': '5551', '5552': '5551', '7777': '5551', '1234': '5678', '5678': '5678', '9012': '5678', '0002': '5678', '8888': '8888', '2468': '5678', '0001': '5678', '0004': '5678', '2222': '5678', '3721': '5678', '2333': '5678', '1236': '5678', '1235': '5678', '6661': '5678', '6662': '5678', '6663': '5678'}

统计家族信息:

 for k, v in dic1.items():
     dic2[v] = dic2.get(v, []) + [k]  # 使用 dic1 构建 dic2,将每个根节点对应的成员列表保存起来(get返回对应的成员)

dic2存储结果如图:

 {'5551': ['6666', '5551', '5552', '7777'], '5678': ['1234', '5678', '9012', '0002', '2468', '0001', '0004', '2222', '3721', '2333', '1236', '1235', '6661', '6662', '6663'], '8888': ['8888']}

dic3存储结果:

 {'6666': [1, 100], '1234': [2, 300], '8888': [1, 1000], '2468': [1, 500], '7777': [2, 300], '3721': [2, 150], '9012': [1, 100], '1235': [1, 50], '2222': [1, 300], '2333': [1, 100]}

输出:

 print(len(dic2))  # 根节点个数即家庭数
 for k, v in dic2.items(): 
     min_member = min(v)
     dic4[min_member] = [len(v), 0, 0]  # 用dic4表示家庭输出情况
     for j in v:
         if j in dic3:
             dic4[min_member][1] += dic3[j][0]
             dic4[min_member][2] += dic3[j][1]
     dic4[min_member][1] /= dic4[min_member][0]   # 计算人均房产套数、人均房产面积
     dic4[min_member][2] /= dic4[min_member][0]
 dic4 = sorted(dic4.items(), key=lambda x: (-x[1][-1], x[0]))  # 家庭信息首先按人均面积降序输出,若有并列,则按成员编号的升序输出
 for i in dic4:
     print("{} {} {:.3f} {:.3f}".format(i[0], i[1][0], i[1][1], i[1][2]))

总的来说,这道题题目信息较多,整理起来很繁琐


7-12 最长对称子串

题目要求:

对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。

输入格式:

输入在一行中给出长度不超过1000的非空字符串。

输出格式:

在一行中输出最长对称子串的长度。

输入样例:

 Is PAT&TAP symmetric?

输出样例:

 11

思路:这可以看成一个dp问题,可用求解dp问题的方法求解:即按照 i 和 j 分别枚举两边端点,更新dp【i】【j】 (表示是否为回文字串)

定义一个函数求出最长对称字串:

 def length(s):
     n = len(s)
     dp = [[False] * n for i in range(n)]  # 创建一个二维数组 dp 用于存储子问题的解,其中dp[i][j] 表示从索引 i 到索引 j 的子串是否是回文子串
     for i in range(n):
         dp[i][i] = True  # 单个字符是对称的,因此初始化 dp[i][i] = True
     max_len = 1  # 初始化变量 max_length 为1,用于记录最长对称子串的长度
     for t in range(2, n + 1):  # 使用两层循环开始填表,外层循环t表示子串的长度,从2开始直到整个字符串的长度
         for i in range(n - t + 1):  # 内层循环 i 表示子串的起始位置
             j = i + t - 1  # 计算 j = i + t - 1 表示子串的结束位置
             if t == 2 and s[i] == s[j]:  # 如果子串长度为2且两端字符相等
                 dp[i][j] = True  # 将 dp[i][j] 设置为True
             elif s[i] == s[j] and dp[i + 1][j - 1]:  # 或者子串的首尾字符相等且内部子串也是回文的
                 dp[i][j] = True  # 将 dp[i][j] 设置为True
             if dp[i][j]:
                 max_len = max(max_len, t)  # 如果 dp[i][j] 为True,更新 max_len 为当前长度 length 的最大值
     return max_len

主程序:

 s = input().strip()
 re = length(s)
 print(re)

算法精品讲解(2)——DP问题入门(适合零基础者,一看就会)

但是我下来再跑一遍的时候发现TLE了,测试时又是AC:

分析时间复杂度为 O(n2),其中 n 是字符串的长度,可能还是时间复杂度过高,还得再改改


  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值