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}")
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
题目要求:
本题要求你计算A−B。不过麻烦的是,A和B都是字符串 —— 即从字符串A中把字符串B所包含的字符全删掉,剩下的字符组成的就是字符串A−B。
输入格式:
输入在2行中先后给出字符串A和B。两字符串的长度都不超过104,并且保证每个字符串都是由可见的ASCII码和空白字符组成,最后以换行符结束。
输出格式:
在一行中打印出A−B的结果字符串。
输入样例:
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函数的详解:
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 是字符串的长度,可能还是时间复杂度过高,还得再改改