目录
[例题 P143 饮料换购](#例题 P143 饮料换购)
[例题 P550 图像模糊](#例题 P550 图像模糊)
[例题 P156 螺旋矩阵](#例题 P156 螺旋矩阵)
[P128 冰雹数](#P128 冰雹数)
三、模拟
模拟题:直接按照题目含义模拟即可,一般不涉及算法
例题 P143 饮料换购
乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊 C 型饮料,凭 3 个瓶盖可以再换一瓶 C 型饮料,并且可以一直循环下去(但不允许暂借或赊账)。
请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的 n 瓶饮料,最后他一共能喝到多少瓶饮料。
输入描述
输入一个整数 n(0<n<1000)n(0<n<1000),表示开始购买的饮料数量。
输出描述
输出一个整数,表示实际得到的饮料数
pythonn=int(input()) number=n while True: if n>=3: n-=3 number+=1 n+=1 else: break print(number)
pythonn=int(input()) number=n while True: if n>=3: number+=n//3 n=n//3+n%3 else: break print(number)
例题 P550 图像模糊
小蓝有一张黑白图像,由 n×m 个像素组成,其中从上到下共 n 行,每行从左到右 m 列。每个像素由一个 0 到 255 之间的灰度值表示。
现在,小蓝准备对图像进行模糊操作,操作的方法为:
对于每个像素,将以它为中心 3×3 区域内的所有像素(可能是 9 个像素或少于 9 个像素)求和后除以这个范围内的像素个数(取下整),得到的值就是模糊后的结果。
请注意每个像素都要用原图中的灰度值计算求和。
输入描述
输入的第一行包含两个整数 n,m。
第 2 行到第 n+1 行每行包含 m 个整数,表示每个像素的灰度值,相邻整数之间用一个空格分隔。
其中,1≤n,m≤100 。
输出描述
输出 n 行,每行 m 个整数,相邻整数之间用空格分隔,表示模糊后的图像。
pythonn, m = map(int, input().split()) image = [] for _ in range(n): image.append(list(map(int, input().split()))) result = [[0] * m for _ in range(n)] for i in range(n): for j in range(m): # 用于存储以当前像素为中心的3x3区域内像素值的总和 total = 0 # 用于统计以当前像素为中心的3x3区域内的像素个数 count = 0 # 遍历以当前像素为中心的3x3区域 for x in range(max(0, i - 1), min(n, i + 2)): for y in range(max(0, j - 1), min(m, j + 2)): total += image[x][y] count += 1 # 计算当前像素模糊后的灰度值,取下整 result[i][j] = total // count for row in result: for num in row: print(num, end=' ') print()
例题 P156 螺旋矩阵
对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
例如,一个 4 行 5 列的螺旋矩阵如下:
python1 2 3 4 5 14 15 16 17 6 13 20 19 18 7 12 11 10 9 8
输入描述
输入的第一行包含两个整数 n,m 分别表示螺旋矩阵的行数和列数。
第二行包含两个整数 r,c,表示要求的行号和列号。
其中,2≤n,m≤1000,1≤r≤n,1≤c≤m。
输出描述
输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。
pythonn, m = map(int, input().split()) r, c = map(int, input().split()) matrix = [[0] * m for _ in range(n)] num = 1 top, bottom, left, right = 0, n - 1, 0, m - 1 while num <= n * m: # 从左到右 for i in range(left, right + 1): matrix[top][i] = num num += 1 top += 1 if num > n * m: break # 从上到下 for i in range(top, bottom + 1): matrix[i][right] = num num += 1 right -= 1 if num > n * m: break # 从右到左 for i in range(right, left - 1, -1): matrix[bottom][i] = num num += 1 bottom -= 1 if num > n * m: break # 从下到上 for i in range(bottom, top - 1, -1): matrix[i][left] = num num += 1 left += 1 print(matrix[r - 1][c - 1])
模拟题作业(上)
P128 冰雹数
任意给定一个正整数 N,
如果是偶数,执行: N/2;
如果是奇数,执行: N×3+1,
生成的新的数字再执行同样的动作,循环往复。
通过观察发现,这个数字会一会儿上升到很高,一会儿又降落下来。
就这样起起落落的,但最终必会落到"1",这有点像小冰雹粒子在冰雹云中翻滚增长的样子。
比如 N=9,
9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1
可以看到,N=9 的时候,这个"小冰雹"最高冲到了 52 这个高度。
输入描述
输入一个正整数 N(N<10^6)。
输出描述
输出一个正整数,表示不大于 NN 的数字,经过冰雹数变换过程中,最高冲到了多少。
简单模拟
pythonN = int(input()) N_all_max = 1 for begin in range(1, N + 1): # 初始值 current = begin N_max = begin # 停止时 while current != 1: # 偶数 if current % 2 == 0: current = current // 2 # 奇数 else: current = current * 3 + 1 # 找每个数的最大值,对比N_max if current > N_max: N_max = current # 找整体的最大值,对比N_all_max if N_max > N_all_max: N_all_max = N_max print(N_all_max)
代码的时间复杂度为 O(n^2)
改进方法
使用记忆:可以创建一个字典来存储已经计算过的数字及其在变换过程中的最大值。这样在遇到重复数字时,直接从字典中获取结果,避免重复计算。
代码时间复杂度为 O(n*k)。这种方法能够避免对任何已经计算过的数字进行重复计算。只要某个数字在之前的计算中出现过,后续再次遇到时就可以直接利用之前的计算结果,无论这个数字是在哪个起始数字的变换过程中出现的。虽然通过字典避免了部分重复计算,但字典的查找和插入操作存在一定的时间开销,而且随着字典中元素数量的增加,这些操作的累积开销也会影响整体性能。
pythonN = int(input()) # 储存已及算过的数字及其最大值 memory={} N_all_max = 1 for begin in range(1, N + 1): current = begin N_max = begin while current != 1: # 排除 if current in memory: N_max=max(N_max,memory[current]) break if current % 2 == 0: current = current // 2 else: current = current * 3 + 1 if current > N_max: N_max = current if N_max > N_all_max: N_all_max = N_max # 记录 memory[begin]=N_max print(N_all_max)
以上纯属浪费时间
整体解题思路
本题的核心目标是计算从 1 到给定整数
N
每个数在经过考拉兹猜想(3n + 1 猜想)变换过程中所达到的最大值。考拉兹猜想的规则是:对于任意一个正整数n
,如果它是偶数,则将它除以 2;如果它是奇数,则将它乘以 3 再加 1,不断重复这个过程,最终会得到 1。为了高效地解决这个问题,需要避免不必要的重复计算,这是提高算法效率的关键。分析重复计算问题的产生
从 1 开始逐个对数字进行考拉兹变换时,在处理较大数字的变换过程中,可能会得到一些之前已经处理过的较小数字。例如,在处理数字 5 的变换时:
5 是奇数,变换后得到
5 * 3 + 1 = 16
。16 是偶数,继续变换得到
16 // 2 = 8
。8 是偶数,再变换得到
8 // 2 = 4
。而在之前处理数字 4 时,已经完整地对 4 进行了考拉兹变换并记录了其变换过程中的最大值。如果不做处理,当在处理 5 时再次遇到 4,又会对 4 进行一次完整的变换,这就产生了重复计算,浪费了计算资源和时间。
解决思路
对于每个要处理的起始数字
begin
,我们将 pass 初始化为begin - 1
。pass 作为一个界限,用于判断在当前begin
的变换过程中得到的新数字是否已经在之前处理过。当current
小于等于 pass 时,说明这个数字在之前处理1
到begin - 1
这些数字的过程中已经被处理过了,其变换过程中的最大值也已经被记录,所以无需再对这个数字继续进行变换,直接结束当前begin
的变换过程。
pythonN = int(input()) N_all_max = 0 for begin in range(1, N + 1): current = begin # 记录当前起始数减 1 的值 n_pass = begin - 1 # 当 n 不等于 1 且大于起始数减 1 时进行循环 while current != 1 and current > n_pass: # 如果当前的 n 大于之前记录的最大值,更新最大值 if current > N_all_max: N_all_max = current if current % 2 == 0: current = current // 2 else: current = current * 3 + 1 print(N_all_max)
由于算法的重点在于全局最大值,而不是每个数字的局部最大值,所以不需要保存每一次循环的最大值。只需要在每次变换过程中,当当前数字大于
N_all_max
时更新N_all_max
即可,最终得到的N_all_max
就是所有数字在变换过程中出现的最大值。代码时间复杂度也为 O(n*k),两种代码的时间复杂度是完全一样的。但实际运行过程中由于使用字典的算法存在字典操作的开销,可能会导致其运行时间更长。