打点计数器的区间合并
问题描述
小明正在设计一台打点计数器,该计数器可以接受多个递增的数字范围,并对这些范围内的每个唯一数字打点。如果多个范围之间有重叠,计数器将合并这些范围并只对每个唯一数字打一次点。小明需要你帮助他计算,在给定的多组数字范围内,计数器会打多少个点。
例如,给定三个数字范围 [1, 4], [7, 10], 和 [3, 5],计数器首先将这些范围合并,变成 [1, 5] 和 [7, 10],然后计算这两个范围内共有多少个唯一数字,即从 1 到 5 有 5 个数字,从 7 到 10 有 4 个数字,共打 9 个点。
测试样例
样例1:
输入:
inputArray = [[1, 4], [7, 10], [3, 5]]
输出:9
样例2:
输入:
inputArray = [[1, 2], [6, 10], [11, 15]]
输出:12
样例3:
输入:
inputArray = [[1, 3], [2, 6], [8, 10]]
输出:9
思路
区间合并
这道题考察的点就是区间合并的实现,最后的计数都是顺带的事情。 区间合并方法较为经典,在此记录以供复习。
def solution(inputArray):
# 区间合并
qj = []
for interval in sorted(inputArray): # 先对区间进行排序
if not qj or interval[0] > qj[-1][1]: # 如果当前区间与最后一个区间不重叠
qj.append(interval)
else:
qj[-1][1] = max(qj[-1][1], interval[1]) # 合并区间
cnt = 0
for q in qj:
cnt += q[1] - q[0] + 1
return cnt
记录打点的位置
可以直接对所有的区间内打过的点进行记录,比如给定一个大的区间[1,100],初始赋值为0,打点的范围是[2,5],[7,10],那么在大的区间内对需要打点的地方+1,最后得到大区间内不为0的位置的个数即为最终答案。
相对于合并区间的方法,时间复杂度要高不少,应为O(mn)。 代码略
优秀项目组初选评比
问题描述
公司正在进行优秀项目组评比的初选工作,参赛者有小C、小U、小R、小S、小M和小F的项目组。评委会已经根据他们提交的材料完成了打分,各个项目组的得分分别是s1,s2,s3,…,sks1,s2,s3,…,sk。评委们希望设定一个初选晋级的分数线xx,让所有得分大于xx的项目组晋级,其他的项目组将被淘汰。此外,评委们希望晋级的项目组数量和淘汰的项目组数量都在区间[m,n][m,n]之间。
显然,这个分数线xx可能不存在,也可能存在多个满足条件的分数线。如果不存在满足条件的xx,则输出−1−1;如果存在多个满足条件的分数线xx,则输出满足条件的最小分数线。
测试样例
示例 1:
输入:
m = 2, n = 3, a = [1, 2, 3, 5, 6, 4]
输出:3
示例 2:
输入:
m = 1, n = 2, a = [7, 8, 9, 3, 5]
输出:-1
示例 3:
输入:
m = 1, n = 4, a = [7, 8, 9, 3, 5]
输出:3
思路
最最暴力的方案
最为简单的方法就是从最小的分数开始,判断每一个数作为及格线能否符合要求,第一个符合要求的及格线就是最终答案。但是时间复杂度较高,极端情况下可达O(n^2)
代码略
二分
对于一个排序后的数组,取任一一个元素作为及格分数,那么其左边(包括自身)的元素就是没有及格的,右边的就是及格了的。那么就可以通过二分的方法来找到最合适的作为及格分数的元素。同时最终的元素也一定是最小的有效及格分数,因为如果比当前元素还小,那么及格的池子和未及格的池子的范围就会发生变化,如果还满足要求的话最小的元素就应该是当前元素的前一个元素,那么结果就不会是当前的元素,而是当前元素的前一个元素。所以就保证了得到的元素一定是最小的。
def solution(m: int, n: int, a: list) -> int:
# write code here
if n * 2 < len(a) or m // 2 > len(a):
return -1
a.sort()
left = 0
right = len(a) - 1
res = -1
while left < right:
mid = (right + left) // 2
left_count = mid + 1
right_count = len(a) - mid - 1
if (m <= left_count <= n) and (m <= right_count <= n):
res = mid
right = mid
elif left_count > n or right_count < m:
right = mid
elif left_count < m or right_count > n:
left = mid + 1
if res == -1:
return -1
else:
return a[res]
注意
考虑到特殊情况,需要增加一些额外的判断。
蛇形填充n阶方阵
问题描述
小U面临一个有趣的任务:在一个 n×nn×n 的方阵中填入 11 到 n×nn×n 这些数字,并要求按照蛇形顺序从右上角开始,沿着方阵的边界顺时针进行填充。蛇形填充的特殊排列方式使得每一层数字呈现出波浪形的排列方式。
例如,当 n=4n=4 时,方阵应如下所示:
10 11 12 1
9 16 13 2
8 15 14 3
7 6 5 4
你需要编写程序输出填充后的方阵,确保格式的整齐性。
测试样例
样例1:
输入:
n = 4
输出:[[10, 11, 12, 1], [9, 16, 13, 2], [8, 15, 14, 3], [7, 6, 5, 4]]
样例2:
输入:
n = 5
输出:[[13, 14, 15, 16, 1], [12, 23, 24, 17, 2], [11, 22, 25, 18, 3], [10, 21, 20, 19, 4], [9, 8, 7, 6, 5]]
样例3:
输入:
n = 3
输出:[[7, 8, 1], [6, 9, 2], [5, 4, 3]]
思路
这道题的关键就在于要实现从上到下、从右到左、从下到上、从左到右四个方向的有序遍历以及边界条件的控制。 方法其实相对比较固定,但是会有点绕,在这里记录一下实现方法,以供后面复习。
def solution(n: int) -> list:
# write code here
res = [[0] * n for _ in range(n)]
cnt = 1
top, bottom, left, right = 0, n - 1, 0, n - 1
while cnt <= n * n:
#从上到下
for i in range(top,bottom+1):
res[i][right] = cnt
cnt += 1
right -= 1
#从右到左
for i in range(right,left-1,-1):
res[bottom][i] = cnt
cnt += 1
bottom -= 1
#从下到上
for i in range(bottom,top-1,-1):
res[i][left] = cnt
cnt += 1
left += 1
#从左到右
for i in range(left,right+1):
res[top][i] = cnt
cnt += 1
top += 1
return res
小Q的非素数和排列问题
问题描述
小C对排列很感兴趣,她想知道有多少个长度为n的排列满足任意两个相邻元素之和都不是素数。排列定义为一个长度为n的数组,其中包含从1到n的所有整数,每个数字恰好出现一次。
测试样例
样例1:
输入:
n = 5
输出:4
样例2:
输入:
n = 3
输出:0
样例3:
输入:
n = 6
输出:24
思路
这里有两个知识点,分别是排列组合问题和素数判断。
有了豆包其实很方便,可以让他直接给出一个用于判断素数的函数,就不用自己再重复的实现。
def is_prime(num):
# 辅助函数:判断一个数是否为素数
if num <= 1:
return False
for i in range(2, int(num**0.5) + 1):
if num % i == 0:
return False
return True
在python中对于排列组合问题可以直接调用函数获取全部的可能组合,非常的方便。 但是如果是用别的语言来实现,或者说没有直接能够用来获取全部组合的函数的时候应该如何去解决这个问题呢?(待解决)
from itertools import permutations
def solution(n: int) -> int:
# 生成所有排列
all_permutations = permutations(range(1, n + 1))
# 统计符合条件的排列数量
count = 0
for perm in all_permutations:
valid = True
for i in range(n - 1):
if is_prime(perm[i] + perm[i + 1]):
valid = False
break
if valid:
count += 1
return count