第一次做竞赛的题,只做了两道半,下面整理下思路
2341. 数组能形成多少数对
主要思路:
第一步:看约束条件
- 题目中给出整个数组的大小为100,其中数字的范围也是[1,100],这表明可以穷举出现的数字。
第二步:看题目内容
- 题目要求按对消除相同元素,我首先想到的是去除重复元素就行。但是如果重复元素的个数是奇数,那么还需要保留一个。因此思路就过渡到统计各个数字出现的次数上了。统计结束后,如果数字出现次数除2为零,需要移去个数加上这个次数;如果不为零,剩下元素的个数加一。
第三步:写代码
class Solution:
def numberOfPairs(self, nums: List[int]) -> List[int]:
res1,res2=0,0
ls=[0]*101
for i in nums:
ls[i]+=1
for i in ls:
res1=res1+(i//2)
res2=res2+(i%2)
return [res1,res2]
2342. 数位和相等数对的最大和
主要思路:
第一步:看约束条件
范围很大要考虑溢出问题。int最大2147483647 大概 2 ∗ 1 0 9 2*10^9 2∗109
第二步:看题目内容
- 题目要求统计数字各位和相同的数字的最大和。首先就要求出各个数字的位数和是多少,然后判断有没有相同的位数和,如果有就加起来和最大的比较。最后返回最大和。
- 统计相同元素,最好的办法应该用桶排序的思想(像第一题),但是由于数字太大,而且稀疏,因此使用字典的方法会更好。
- 首先,定义一个字典,键为数字位数和的字符格式,值为真实值。然后遍历nums,如果数字的位数和不在字典中,就加入字典。如果存在就拿真实值和字典中的值相加,判断是否是最大和(此处需要定一个当前最大和用于比较),同时在字典中保留两个相同值中最大的一个,以保证后续还有相同值出现时,加和是最大的。
第三步:写代码
class Solution:
def maximumSum(self, nums: List[int]) -> int:
dic={
}
res=-1
for i in nums:
tmp=sum([int(k) for k in list(str(i))])
if str(tmp) in dic:
res=max(res,dic[str(tmp)]+i)
dic[str(tmp)]=max(dic[str(tmp)],i)
else:
dic[str(tmp)]=i
return res
2343. 裁剪数字后查询第 K 小的数字
主要思路:
第一步:看约束条件
列表中字符串的最大长度是100,超过了整型的记录范围
裁截的位置 t r i m i trim_i trimi 在字符的长度内
位置的选取 k i k_i ki在列表的长素内
有了这些条件就知道问题的边界在哪里了
第二步:看题目内容
- 题目要求出裁剪后第k小的数字的原始位置。这个就设计到一个原始位置的问题。
- 注意分析题目的特殊情况,如果出现重复的数字,默认原始位置索引小的,值更小。如下表所示,此处1号位置上的2小于4号位置上的2。排序之后顺序变为【1,2,2,3,4】如果返回第3小的数的原始位置,应该返回的是4。所以单使用list.index()是没办法找到重复元素的真实位置的。因此不能使用排序函数直接对数组进行排序,应该记录位置,只对位置进行排序。
索引 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
值 | 1 | 2 | 3 | 4 | 2 |
-
了解完前提条件之后,这个题目类似于基数排序。裁剪的位数就是基数排序迭代的次数。
因此定义一个桶大小为 [ 0 − 9 ] [0-9] [0−9]。另外因此定一个数组loc记录每次排序的位置, l o c [ i ] [ j ] loc[i][j] loc[i][j] ,表示裁切长度为i时,第j小的数字的原始位置 -
首先初始化位置,将当前位置写入 l o c [ 0 ] [ : ] loc[0][:] loc[0][:]
-
然后取所有元素的最后一位放入对应的桶中,然后按0-9的顺序重新排列数组,这里只记录排序后的数序写入 l o c [ 1 ] [ : ] loc[1][:] loc[1][:]。以上表为例,那么loc的构成为
loc[0] | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
原始值 | 1 | 2 | 3 | 4 | 2 |
loc[1] | 0 | 1 | 4 | 2 | 3 |
排序后值 | 1 | 2 | 2 | 3 | 4 |
如果裁切为[1,1] 那么就是取 l o c [ 1 ] [ 1 − 1 ] loc[1][1-1] loc[1][1−1] 也就是0
第三步:写代码
class Solution:
def smallestTrimmedNumbers(self, nums: List[str], queries: List[List[int]]) -> List[int]:
n = len(nums)
m = len(nums[0])
# vecs[i][j] 表示基数排序第 i 轮中第 j 小的数对应的下标
vecs=[[i for i in range(0,n)]]
for i in range(1,m+1):
B={
}
for ele in range(10):
B[str(ele)]=[]
# 把第 i - 1 轮的结果,根据 nums 中右数第 i 位数,依次放入桶中
for x in vecs[i - 1]:
B[nums[x][m - i]].append(x);
# 把每个桶的结果连接起来,成为第 i 轮的结果
tmp=[]
for j in range(10):
for x in B[str(j)]:
tmp.append(x)
vecs.append(tmp)
ans=[]
for q in queries:
ans.append(vecs[q[1]][q[0] - 1])
return ans
2344. 使数组可以被整除的最少删除次数
主要思路:
第一步:看约束条件
- 题目中的约束条件,除数和被除数都很大,可能会超时
第二步:看题目内容
- 题目要求返回可以被numsDivide数组整除的nums中最小元素,最直接的思路就是选择nums中最小的元素,如果可以被整除,那么就返回该值。如果不能被整除,那么就删除该元素,继续重复上述步骤,代码如下
class Solution:
def minOperations(self, nums: List[int], numsDivide: List[int]) -> int:
count=0
while len(nums)>0:
su=0
k=min(nums)
for i in numsDivide:
su+=(i%k)
if su==0:
return count
nums.remove(k)
count+=1
return -1
但是如果数据量非常大时,就会超时。
2. 换个思路,如果某个数字可以被numsDivide整除,那么也可以被他们的最大公约数整除。因此,先求numsDivide的最大公约数,将遍历numsDivide数组转化为仅除最大公约数。
3. 然后遍历一遍数组,找到所有可以被最大公约数整除的数,求他们的最小值记为mn。如果mn为0,那么就返回-1。否则统计nums中小于mn的数量,即需要删除的元素数量。
第三步:写代码
class Solution:
def minOperations(self, nums: List[int], numsDivide: List[int]) -> int:
g=gcd(*numsDivide)
mn=min([x for x in nums if g%x==0 ], default=0)
if mn==0:
return -1
return sum([ x< mn for x in nums])
补充下python中" * "的用法(参考原文)
1.单星号 * 用于取出列表list或元组tuple中的元素。
单星号 * 只能读取到字典中的键(key)。
a = [1,2]
print(*a)
# 输出 1 2
2.用于收集列表中多余的值,收集的是列表。
a,b,*l=[2,3,4,5]
print(a) # 2
print(b) # 3
print(l) # l是列表 [4,5]
3.在函数中,*代表收集参数,将收集的参数放在一个元组tuple里面。
def printFunc(x,*para):
print(x)
print(para)
printFunc(1,2,3)
# 输出
# 1
# (2, 3)
printFunc(x = 1,2,3,4) #报错