algorithm2024cpp17/src/11/P9869/P9869.md
2024-12-01 00:29:43 +08:00

4.0 KiB
Executable File
Raw Blame History

这个问题涉及三值逻辑True、False、Unknown变量的赋值与操作需要找到一种初始赋值方案使得在执行所有操作后每个变量的最终值与初始值相同并且使初始赋值中 Unknown 的变量尽可能少。

问题分析

  1. 变量与操作

    • n 个变量,每个变量的值可以是 TFU
    • m 条操作,这些操作包括直接赋值(如 x_i ← T)、赋值为另一个变量的值(如 x_i ← x_j)、或赋值为另一个变量的逻辑非(如 x_i ← ¬x_j)。
  2. 目标

    • 执行所有操作后,所有变量的最终值与初始值相同。
    • 在满足上述条件的前提下,初始赋值中 Unknown 的变量数最少。

思路概述

为了满足最终值等于初始值,需要建立变量之间的依赖关系,并找出这些关系中的约束条件。可以将变量及其值的赋值关系看作是一个等式系统,通过这些等式来确定变量的可能取值。为了有效管理这些等式并高效求解,使用 并查集Union-Find 是一种常见的方法。

具体步骤

  1. 表示三值逻辑

    • TFU 分别映射为不同的整数值,便于在代码中处理:
      • T 被表示为 100001
      • F 被表示为 -100001
      • U 被表示为 0
  2. 并查集初始化

    • 使用一个数组 fa 来表示每个变量的父节点或代表元素。初始时,每个变量的父节点指向自己。
  3. 处理每条操作

    • 直接赋值操作TFU
      • 如果操作是 x_i ← T,则将 fa[x_i] 设为 T 的表示值。
      • 类似地处理 x_i ← Fx_i ← U
    • 赋值为另一个变量的值+ 操作):
      • 如果操作是 x_i ← x_j,则将 fa[x_i] 设为 fa[x_j],表示 x_ix_j 的值相同。
    • 赋值为另一个变量的非值- 操作):
      • 如果操作是 x_i ← ¬x_j,则将 fa[x_i] 设为 -fa[x_j],表示 x_ix_j 值的逻辑非。
  4. 查找变量的最终值

    • 使用 find 函数来查找变量的代表值。
    • find 函数中:
      • 对于 TF,直接返回其表示值。
      • 对于 U,返回 0
      • 对于其他情况,通过递归查找父节点,处理逻辑非的情况,并使用 book 数组避免死循环。
  5. 计算 Unknown 的最小数量

    • 遍历所有变量,使用 find 函数确定每个变量的最终值。
    • 如果某个变量的最终值为 U,则计数。
    • 输出所有测试数据中 Unknown 变量的最小数量。

关键细节与优化

  • 处理逻辑非(¬

    • 逻辑非会改变变量的值,因此在并查集中需要表示变量与其非值的关系。
    • 使用负值表示逻辑非关系,例如 x_i ← ¬x_j 表示 fa[x_i] = -fa[x_j]
  • 防止死循环

    • 当变量的代表值可能因逻辑非关系而相互指向时,可能会引发无限递归。通过 book 数组记录已经访问过的节点,从而避免死循环。
  • 高效的查找操作

    • 使用路径压缩技术优化 find 函数,使得查找操作的时间复杂度接近常数时间,确保算法在大规模数据(n, m ≤ 10^5)下依然高效。

示例解析

以第一个样例为例:

3 3
- 2 1
- 3 2
+ 1 3
  • 操作步骤:

    1. x_2 ← ¬x_1fa[2] = -fa[1]
    2. x_3 ← ¬x_2fa[3] = -fa[2]
    3. x_1 ← x_3fa[1] = fa[3]
  • 通过并查集的合并与查找,最终确定每个变量的值,使得初始值与最终值相同,并且 Unknown 的数量最少。在该示例中,可以赋予 x1 = Tx2 = Fx3 = T,使得 Unknown 的数量为 0

总结

该解决方案通过并查集有效地管理变量之间的赋值与逻辑关系,处理了直接赋值、变量赋值以及逻辑非赋值的情况。通过高效的查找与合并操作,能够在大规模数据下快速求解,同时通过合理的表示与优化,确保了算法的正确性与效率。