题目大意
Q老师编写的软件中存在着N个模块,依次编号为1..N,其中编号为i的模块会在接收到一个名称为Signal[i]的信号时启动,对于不同的模块,他们启动需要的信号可能是相同的。 在这N个模块里,代码的执行也会触发若干(K<=5)信号,这些信号又可能会导致其他模块的启动,从而使得整个软件运转起来,但值得庆幸的是,模块间的互相启动是不存在循环的,也就是无论收到什么样的信号,这个软件最终都会停止运行。 现在Q老师为了测试整个软件,使用特定的工具发出了M个信号,而他希望能够知道,每个模块应该被运行的次数。
解题思路
通过题意,我们首先可以确定,不同模块之间可以按照以下原则构成一个有向无环图:
- 初始信号流,即 M 个初始信号,我们将其设定为模块
0
发出的信号 - 若模块
i
发出的信号能够使得模块j
被激活,我们连接一条从i
到j
的有向边(i,j)
比如数据:
3 2
123 256
123 2 456 256
456 3 666 111 256
256 1 90
对应的图为:
由于原题中给出的信号在0~10^5之间,因此我们可以用一个大小为10^5的数组signList
来记录信号可以激活的模块,辅助我们构造整个图:
Input n
Input m
// 将初始数据流做为 module 0
For i = 1 .. m
Input sign
module[0].sendSign.push(sign)
End For
// 读取其他module的信息
For i = 1 .. n
Input sign
module[i].activeSign = sign
signList[ sign ].canActiveModule.push(i)
Input num
For j = 1 .. num
Input sign
module[i]sendSign.push(sign)
End For
End For
// 构造有向无环图
For i = 0 .. n
For sign in module[i].sendSign
For j in signList[ sign ].canActiveModule
addEdge(i, j)
End For
End For
End For
在得到有向无环图之后,一个简单的做法是直接在上面做一次DFS,去统计每个点被访问到的次数:
DFS(nowModule):
If (nowModule not 0) Then
activeCount[ nowModule ] = activeCount[ nowModule ] + 1
End If
For each j in (nowModule, j)
DFS(j)
End For
该算法的时间复杂度非常高,但由于本题没有设计专门针对的数据,所以在测试时也能通过所有的测试点。
但是显然这不是我们要的最优算法,本题实际考察的算法为拓扑排序(toposort)。
利用拓扑排序,在O(n + m)的时间内计算出所有点被访问的次数,具体的算法讲解可以参见hiho一下第48期
在本题中,访问次数对应的为第48期题目中的病毒数量。因此我们在构造完图之后,可以使用同样的算法来解决:
// 在构造图时同时统计入度
For i = 0 .. n
For sign in module[i].sendSign
For j in signList[ sign ].canActiveModule
addEdge(i, j)
inDegree[j] = inDegree[j] + 1
End For
End For
End For
// 进行拓扑排序
tail = 0;
For i = 0 .. n // 这里一定要从0开始,因为Module 0也是图中的点
If (inDegree[i] == 0) Then // 入度为0的点
sequence[tail] = i
tail = tail + 1
End If
End For
activeCount[0] = 1 // 设定初始信号流的访问次数为1
activeCount[1 .. n] = 0
i = 0
While (i < tail) Then
nowModule = sequence[i]
For each j in (nowModule, j)
activeCount[j] = activeCount[j] + activeCount[ nowModule ]
inDegree[j] = inDegree[j] - 1
If (inDegree[j] == 0) Then
sequence[tail] = j
tail = tail + 1
End If
End For
i = i + 1
End While
最后再将activeCount
数组依次输出即可。由于本题有多组数据,在实现时一定要注意初始化。
可能存在一个一开始就已经启动的节点?