2022-01-08 每日打卡:难题精刷
写在前面
“这些事儿在熟练之后,也许就像喝口水一样平淡,但却能给初学者带来巨大的快乐,我一直觉得,能否始终保持如初学者般的热情、专注,决定了在做某件事时能走多远,能做多好。” 该系列文章由python编写,所刷题目共三个来源:之前没做出来的 ;Leetcode中等,困难难度题目; 周赛题目;某个专题的经典题目,所有代码已AC。每日1-3道,随缘剖析,希望风雨无阻,作为勉励自己坚持刷题的记录。
HAOI 2008 小朋友分糖
这里的中位数的性质可以使用贪心的思想证明:
对于任意两个数,只要该点位于这两个数的中间,就可以让它们取得最小值。
所以即使是偶数的中位数,也可以不那么严谨的直接取两个数中的任意一个。
n, data = eval(input()), []
for _ in range(n):
data.append(eval(input()))
avg, c, pre_sub = sum(data)//n, [], 0
for _ in range(n):
pre_sub+=data[_]
c.append(_*avg-pre_sub)
c.sort()
ans, med = 0, c[n//2]
for _ in c:
ans += abs(_-med)
print(ans)
2127. 参加会议的最多员工数
思路
有向图的问题,首先思路比较好找,注意两种特殊情况即可:
总结一下:
- 情况1:小于等于2的环容他,因为两个人挨着坐,旁边仍可以加入新的集团
- 情况2:大于3的环排他,因为喜欢成环,无法中断
则答案为:max(max_len(长度大于2的环),sum(长度为2的环+长链))
编码阶段
整体框架:
- 排除不在环上的,这里可以使用
拓扑排序
找到最后的环的位置,记录链的长度。如果最后是双向环,加入total作为第1种情况。 - 如果最后不是双向环,那么进行BFS不断统计环的长度。
拓扑排序:
区别于1192. 查找集群内的「关键连接」,这里只需要统计个数,而不需要找出究竟是哪条道路,所以无须不同的id来区分不同的环(目的是为了找到环的入口和出口),只需要沿着环上任何一个点开始不断找就可以统计。
class Solution:
def maximumInvitations(self, favorite: List[int]) -> int:
n = len(favorite)
# used记录是否遍历过,f用于记录链的长度
used, f = [False] * n, [1] * n
# 统计入度,便于进行拓扑排序
indeg = [0] * n
for i in range(n):
indeg[favorite[i]] += 1
# 所有开头的位置,无入度
q = deque(i for i in range(n) if indeg[i] == 0)
while q:
u = q.popleft()
used[u] = True
v = favorite[u]
# 状态转移
f[v] = max(f[v], f[u] + 1)
# 不断寻找新的拓扑起点
indeg[v] -= 1
if indeg[v] == 0:
q.append(v)
# total 表示所有环大小为 2 的「基环内向树」上的最长的「双向游走」路径之和
# ring 表示最大的环的大小
total = ring = 0
for i in range(n):
if not used[i]:
j = favorite[i]
# favorite[favorite[i]] = i 说明环的大小为 2
if favorite[j] == i:
total += f[i] + f[j]
used[i] = used[j] = True
# 否则环的大小至少为 3,我们需要找出环
else:
u = i
cnt = 0
while True:
cnt += 1
u = favorite[u]
used[u] = True
if u == i:
break
ring = max(ring, cnt)
return max(ring, total)