现代编程语言允许编写包含多个执行线程的程序。这就像有多个程序在同一个地址空间中并行运行,访问相同的变量。通常,线程之间需要相互同步。例如,一个线程可能需要等待另一个线程完成某些计算并将结果存储到某个变量中。
线程同步最简单的工具是互斥锁(mutex)。互斥锁是一种特殊对象,可以处于锁定(locked)或解锁(unlocked)状态。一个被锁定的互斥锁始终由且仅由一个线程拥有。线程可以对互斥锁执行两种操作:LOCK 和 UNLOCK。
当线程对一个当前处于解锁状态的互斥锁执行 LOCK 操作时,该互斥锁变为锁定状态,并且该线程获得互斥锁的所有权。如果线程尝试对一个已经被其他线程锁定的互斥锁执行 LOCK 操作,则该线程会被阻塞,直到互斥锁被解锁。
当线程对一个由其拥有的互斥锁执行 UNLOCK 操作时,该互斥锁变为解锁状态。如果有其他线程正在等待对该互斥锁执行 LOCK 操作,则其中一个线程将获得互斥锁的所有权。如果有多个线程在等待,则会随机选择其中一个。
多线程程序中常见的问题之一是死锁(deadlock)。当两个或多个线程相互等待对方释放互斥锁,且它们都无法继续执行时,就会发生死锁。当一个线程正在等待一个被另一个已经终止且未释放互斥锁的线程所锁定的互斥锁时,也会发生死锁。
任务:给定一些线程的描述,你的任务是判断是否可能发生死锁。
每个线程都是以下形式的指令序列:
LOCK <mutex>
UNLOCK <mutex>
你可以假设关于命令的以下条件: 互斥锁的名称为大写字母 A...Z; 没有线程会尝试锁定它已经拥有的互斥锁; * 没有线程会尝试解锁它不拥有的互斥锁。
输入格式
输入的第一行包含线程数量 $M$ ($1 \le M \le 5$),随后是描述每个线程的 $M$ 个数据块。描述线程 $i$ 的数据块的第一行包含该线程中的指令数量 $N_i$ ($1 \le N_i \le 10$),随后是 $N_i$ 行指令。指令中不包含多余的空格。
输出格式
输出的第一行必须包含一个数字:$D$。如果可能发生死锁,$D$ 必须为 $1$,否则为 $0$。
如果可能发生死锁,第二行必须描述一个发生死锁的程序状态。如果存在多个死锁状态,输出其中任意一个即可。在这种情况下,我们寻找的是完全死锁,即没有任何线程可以继续执行——每个线程要么已经终止,要么被互斥锁阻塞。如果不可能发生死锁,该行必须为空。
程序状态通过指定每个线程当前指令的从零开始的索引来描述,顺序与输入文件中给出的线程顺序一致。对于已终止的线程,输出 $-1$ 作为索引。索引必须在同一行上并用空格分隔。
样例
输入 1
2 1 LOCK X 2 LOCK Y LOCK X
输出 1
1 -1 1
说明:如果程序没有解决任何可能发生死锁的测试用例,那么它在没有死锁的测试用例中将无法获得分数。