P4819 杀人游戏 解题报告
P4819 杀人游戏 解题报告
1. 问题解读:我们要解决什么?
首先,我们用一个更简单的模型来理解这个问题。想象一下:
- N个人:一个有N个节点的有向图,每个节点代表一个人。
- 认识关系:
x
认识y
,表示有一条从节点x
到节点y
的有向边。 - 杀手:图中有一个节点是“黑点”(杀手),其余都是“白点”(平民)。每个节点是黑点的概率都是一样的,即
1/N
。 - 查证:我们选择一个节点进行“询问”。
- 如果问到白点,它会告诉我们它所有认识的人(它指向的所有节点)是黑是白。这是一个安全的行动。
- 如果问到黑点,游戏失败(警察被杀)。这是一个危险的行动。
- 目标:在保证警察绝对安全(即永远不直接询问到黑点)的前提下,找出那个黑点。我们要求的是,能成功做到这一点的最大概率是多少。
成功的概率 = 1 - 失败的概率。
失败的概率 = 必须去问一个点,而这个点恰好是杀手的概率。
所以,我们的目标是最小化我们必须冒着风险去“猜”的点(即进行危险询问)的数量。
2. 核心思路:化繁为简
第一步:抱团取暖 -> 强连通分量与缩点
在图中,如果一些人形成了一个“圈子”,在这个圈子里,从任何一个人出发,沿着认识关系最终都能认识到圈子里的其他任何人,我们就称之为一个强连通分量 (SCC)。
这有什么用呢?假设我们询问了这个圈子里的任何一个平民,我们就能知道他认识的人的身份。而通过他认识的人,我们又能知道更多人的身份……最终,整个圈子里所有人的身份都会被揭晓。
所以,我们可以把每个这样的“圈子”看作一个整体,或者说一个“超级节点”。这就是图论中的缩点操作。通过这个操作,原来的复杂图会变成一个没有环的有向无环图 (DAG)。
小结:将原图缩点,把每个强连通分量看作一个点。图中人的数量
N
不变,但我们分析的对象变成了更简单的DAG。
第二步:顺藤摸瓜 -> 找到最佳起点
在缩点后的DAG中,我们应该先问谁呢?
假设有一个超级节点B
,同时有另一个超级节点A
指向它(A -> B
)。我们应该先问A
还是B
?
- 策略1:先问B。如果
B
是杀手,我们失败。如果B
是平民,我们知道了B
里所有人的身份,但对A
一无所知,之后可能还得问A
,多冒一次风险。 - 策略2:先问A。如果
A
是杀手,我们失败。但如果A
是平民,我们不仅知道了A
里所有人的身份,还顺便知道了B
里所有人的身份(因为A
认识B
)。我们用一次安全的询问,获得了两个节点的信息,并且避免了对B
的危险询问。
显然,策略2更优。这个道理告诉我们:对于任何有“上游”节点(有边指向它)的节点,我们都不应该把它作为初始询问点。
最优的策略是,只对那些没有任何“上游”的节点进行初始的危险询问。在图论里,这些节点就是入度为0的节点。
小结:在缩点后的DAG中,我们只需要对入度为0的“超级节点”进行危险询问。问完这些,其他的节点都可以通过安全的“顺藤摸瓜”来查清。
3. 计算概率:基本公式
假设经过缩点后,我们发现有 c
个入度为0的超级节点。根据上面的分析,我们必须对这 c
个超级节点里的人进行危险询问,才有可能揭开整个图的秘密。
如果杀手恰好在这 c
个节点所包含的 c
个人中(假设每个这样的超级节点都只包含一个人),那么我们就有可能问到他导致失败。
- 危险的人数:
c
- 总人数:
N
- 失败的概率(杀手是这
c
个人之一):c / N
- 成功的概率(杀手不在这
c
个人之中):(N - c) / N
这已经非常接近答案了,但还有一个特殊情况。
4. 特殊情况:无需询问的“嫌疑人”
考虑一个非常特殊的情况:
- 有一个人
p
,他自己构成一个大小为1的强连通分量(他不和任何人抱团)。 - 这个
p
是入度为0的(没人认识他)。 p
认识的所有人(或组织),都同时被其他入度为0的人/组织所认识。换句话说,p
指向的所有节点的入度都大于等于2。
在这种情况下,我们其实可以把p
留到最后。我们的策略变成:
- 先去问所有其他的入度为0的节点。
- 在询问过程中,如果我们已经找到了杀手,那太好了,游戏结束,我们成功了,根本不需要去管
p
。 - 如果我们问完了所有其他入度为0的节点以及他们牵扯出的所有人,发现都没问题,但杀手还没找到。此时,只剩下
p
没有被调查。根据排除法,杀手必定是p
!
最关键的是,我们通过推理得知p
是杀手,而根本没有去问他,因此警察是安全的。所以,p
这个点虽然是入度为0,但我们无需对他进行危险询问。
这样一来,我们就省掉了一次危险询问!需要冒风险的点从 c
个减少到了 c - 1
个。
注意: 这种“优惠”只能享受一次。即使有多个满足条件的p
,我们也只能减少一次危险询问。因为只要留一个这样的点到最后用于排除法就够了。
5. 最终算法总结
- 读入数据:建立原始的有向图。
- 缩点:使用Tarjan算法找出所有强连通分量,并将原图缩成一个DAG。同时记录每个强连通分量的大小(
sze
)和每个点属于哪个分量(col
)。 - 建新图:根据缩点结果,建立新的DAG。遍历所有原始边,如果一条边的两个端点属于不同的强连通分量,就在新图中连接这两个分量。
- 注意:要处理重边。从分量A到分量B可能有多条原始边,但在新图中只能算作一条。否则会错误地计算入度。代码中通过一个临时标记数组
vv
来避免重复加边。
- 注意:要处理重边。从分量A到分量B可能有多条原始边,但在新图中只能算作一条。否则会错误地计算入度。代码中通过一个临时标记数组
- 统计入度为0的点:计算新图中每个节点的入度,统计入度为0的节点数量,记为
cnt
。 - 检查特殊情况:
- 遍历所有入度为0的节点。
- 如果找到一个节点
i
满足:- 它的大小
sze[i]
为 1。 - 它指向的所有下游节点
j
的入度inv[j]
都大于1。
- 它的大小
- 如果找到了这样的节点,那么就将
cnt
减 1,并且停止检查(因为优惠只有一次)。
- 计算结果:最终必须进行危险询问的人数就是
cnt
。成功的最大概率为(N - cnt) / N
。输出结果。
这个解题思路完美地结合了图论知识和逻辑推理,最终得到了在最优策略下的最大成功概率。