思路:
- 将每个员工作为节点,喜欢的关系作为边,显然是能够组成若干张连通图的,关键就在于如何理解一张图
- 首先要证明:任何一个第一步构成的图必是一个有且仅有一个环的连通图(如下面图片所示,也称为基环内向树),因为每个员工必有且仅有一个喜欢的员工,即每个节点的出度有且仅为 1,最差情况下前 n 个节点相连不构成环,但此时最后一个节点出度为0(它至少为1),因此必能构成环;对于节点环(蓝色节点)来说,所有的其他节点(绿色节点)都是指向这个环的,如果是从环指向外面,那么环中指向外面的点的出度就大于 1 了,不满足条件,所以如果有第二个环(即绿色节点构成环),那么就存在环指向环的情况,这种情况下就会有环的出口节点出度大于 1 ,综上,有且仅有一个环。
- 在2的前提上,显然如果要选择一个绿色的节点参加会议,那么根据有向边的传递,最终必然会传到蓝色的节点;如果选择一个蓝色节点,同样的根据有向边的传递会传到它本身。综上,我们可以发现选绿的就必须要选蓝的,选蓝的就必须要把所有蓝的全都选了,但把所有蓝的全都选了的话,由于一个员工左右两边只能坐两个人(对于节点来说就是只能连两条边),所以不管环是大是小,我们只能选择全部的蓝色节点
- 特殊情况,当环的大小为 2 时,它是可以接受绿色节点的,因为在环中它的左右两边是同一个节点,因此在这种情况下的选择是两个蓝色节点 + 分别连接这两个点的最长的绿色节点路径。总结引用一下评论区的一句话,两个互相暗恋的人能稳定各自吊着一群舔狗不崩盘,三个和三个以上火药味就没法隐藏,路人只能看戏插不进去
- 计算环的大小,首先要找到环,需要了解图的拓扑排序,拓扑排序有一个要求,就是图必须是有向无环的,那么如果对有向有环图作拓扑排序会发生什么呢?你会对所有绿色节点进行排序,然后剩下的节点就是组成环的蓝色节点
- 假设 f(i) 表示到节点 i 为止的最长边,计算当环的大小为 2 时,可以选择的最大节点数为 f(i) + f(j),其中 i,j 构成环,我们可以发现在第五步的拓扑排序中,我们一个一边拓扑一边以动态规划的形式来计算 f(i)
- 由于总的我们可能会得到很多张图,那么对于环的大小大于2的图来说,我们只能选择一个这样的图,因为它们自成环,需要绕着桌子坐一圈,中间不能有其他人插入;对于环的大小等于2的图来说,它是构成一条链的,头尾都只有一边是固定的,另一边谁坐没有限制;综上,选择最大的大环图或者所有小环图之和,两者取大
class Solution:
def maximumInvitations(self, favorite: List[int]) -> int:
n = len(favorite)
indegree = [0] * n
for i in range(n):
indegree[favorite[i]] += 1
tp = deque()
dp = [1] * n
for i in range(n):
if indegree[i] == 0:
tp.append(i)
while len(tp) > 0:
t = tp.popleft()
dp[favorite[t]] = max(dp[favorite[t]], dp[t] + 1)
indegree[favorite[t]] -= 1
if indegree[favorite[t]] == 0:
tp.append(favorite[t])
ans1, ans0 = 0, 0
for i in range(n):
if indegree[i] != 0:
num = 0
t = i
while indegree[t] != 0:
indegree[t] = 0
t = favorite[t]
num += 1
if num > 2:
ans1 = max(ans1, num)
else:
ans0 += dp[t] + dp[favorite[t]]
return max(ans0,ans1)