区间动态规划例题:布尔括号化问题
字数 2544 2025-10-26 19:15:23

区间动态规划例题:布尔括号化问题

题目描述
给定一个布尔表达式,由符号 T(真)、F(假)以及运算符 &(与)、|(或)、^(异或)组成。表达式以字符串形式给出,例如 "T|F&T^F"。要求计算该表达式有多少种括号化方式,使得最终结果为 True(真)。括号化是指在运算符两侧添加括号,改变运算顺序。例如,对 "T|F&T",可以括号化为 "(T|F)&T""T|(F&T)",两种顺序可能产生不同的布尔值。你需要统计所有能得到 True 的括号化方案数量。

解题思路

  1. 问题分析:表达式由布尔值和运算符交替组成。括号化本质是确定运算顺序,每种顺序对应一个表达式求值结果。我们需要统计结果为 True 的顺序数量。
  2. 区间动态规划定义:设 dp[i][j][0] 表示子表达式 s[i:j+1](包含端点)所有括号化方式中得到 False 的数量,dp[i][j][1] 表示得到 True 的数量。目标是求 dp[0][n-1][1],其中 n 为字符串长度。
  3. 区间划分:对于区间 [i, j],枚举区间内的每个运算符位置 kki+1 开始,每隔2移动,因为运算符间隔出现),将区间分为左部分 [i, k-1] 和右部分 [k+1, j],运算符为 s[k]。根据运算符的布尔运算规则,组合左右部分的结果数量。
  4. 合并方式
    • 运算符 &:结果为 True 当且仅当左右均为 True
      true_count = left_true * right_true
    • 运算符 |:结果为 True 当左右至少一个为 True
      true_count = left_true * right_total + left_total * right_true - left_true * right_true
      (其中 left_total = left_true + left_false,右同)
    • 运算符 ^:结果为 True 当左右布尔值不同:
      true_count = left_true * right_false + left_false * right_true
      结果为 False 的数量可通过 false_count = left_total * right_total - true_count 计算。
  5. 初始化:单字符区间(即 i == j)时,若 s[i] == 'T',则 dp[i][i][1] = 1dp[i][i][0] = 0;若 s[i] == 'F',则 dp[i][i][1] = 0dp[i][i][0] = 1
  6. 计算顺序:按区间长度 len 从小(1)到大(n)遍历,每次处理固定长度的所有区间。

详细步骤

  1. 解析字符串,记录长度 n
  2. 初始化三维数组 dp[n][n][2],全部置0。
  3. 处理长度为1的区间(基础情况):
    • 对于每个 i0 <= i < n,且 i 为偶数,因为布尔值在偶数索引):
      • s[i] == 'T'dp[i][i][1] = 1
      • s[i] == 'F'dp[i][i][0] = 1
  4. 遍历区间长度 len = 3, 5, 7, ..., n(因为表达式长度必为奇数):
    • 遍历区间起点 i,使得 i + len - 1 < n
      • 设区间终点 j = i + len - 1
      • 遍历区间内的运算符位置 kki+1j-1,步长为2):
        • 左子区间 [i, k-1],右子区间 [k+1, j]
        • 根据 s[k] 的运算符类型,计算左右子区间组合后的 true_countfalse_count
          • s[k] == '&'
            true_count = dp[i][k-1][1] * dp[k+1][j][1]
            false_count = (dp[i][k-1][0] + dp[i][k-1][1]) * (dp[k+1][j][0] + dp[k+1][j][1]) - true_count
          • s[k] == '|'
            left_total = dp[i][k-1][0] + dp[i][k-1][1]
            right_total = dp[k+1][j][0] + dp[k+1][j][1]
            true_count = dp[i][k-1][1] * right_total + left_total * dp[k+1][j][1] - dp[i][k-1][1] * dp[k+1][j][1]
            false_count = left_total * right_total - true_count
          • s[k] == '^'
            true_count = dp[i][k-1][1] * dp[k+1][j][0] + dp[i][k-1][0] * dp[k+1][j][1]
            false_count = (dp[i][k-1][0] + dp[i][k-1][1]) * (dp[k+1][j][0] + dp[k+1][j][1]) - true_count
        • true_countfalse_count 累加到 dp[i][j][1]dp[i][j][0]
  5. 最终结果为 dp[0][n-1][1]

示例验证
以表达式 "T|F" 为例(n=3):

  • 初始化:dp[0][0][1]=1T),dp[2][2][0]=1F)。
  • 区间 [0,2],运算符 k=1|
    • left_total = 1right_total = 1
    • true_count = 1*1 + 1*1 - 1*1 = 1
    • dp[0][2][1] = 1
      结果有1种方式得到 True,符合预期(T|F 为真)。
区间动态规划例题:布尔括号化问题 题目描述 给定一个布尔表达式,由符号 T (真)、 F (假)以及运算符 & (与)、 | (或)、 ^ (异或)组成。表达式以字符串形式给出,例如 "T|F&T^F" 。要求计算该表达式有多少种括号化方式,使得最终结果为 True (真)。括号化是指在运算符两侧添加括号,改变运算顺序。例如,对 "T|F&T" ,可以括号化为 "(T|F)&T" 或 "T|(F&T)" ,两种顺序可能产生不同的布尔值。你需要统计所有能得到 True 的括号化方案数量。 解题思路 问题分析 :表达式由布尔值和运算符交替组成。括号化本质是确定运算顺序,每种顺序对应一个表达式求值结果。我们需要统计结果为 True 的顺序数量。 区间动态规划定义 :设 dp[i][j][0] 表示子表达式 s[i:j+1] (包含端点)所有括号化方式中得到 False 的数量, dp[i][j][1] 表示得到 True 的数量。目标是求 dp[0][n-1][1] ,其中 n 为字符串长度。 区间划分 :对于区间 [i, j] ,枚举区间内的每个运算符位置 k ( k 从 i+1 开始,每隔2移动,因为运算符间隔出现),将区间分为左部分 [i, k-1] 和右部分 [k+1, j] ,运算符为 s[k] 。根据运算符的布尔运算规则,组合左右部分的结果数量。 合并方式 : 运算符 & :结果为 True 当且仅当左右均为 True : true_count = left_true * right_true 运算符 | :结果为 True 当左右至少一个为 True : true_count = left_true * right_total + left_total * right_true - left_true * right_true (其中 left_total = left_true + left_false ,右同) 运算符 ^ :结果为 True 当左右布尔值不同: true_count = left_true * right_false + left_false * right_true 结果为 False 的数量可通过 false_count = left_total * right_total - true_count 计算。 初始化 :单字符区间(即 i == j )时,若 s[i] == 'T' ,则 dp[i][i][1] = 1 , dp[i][i][0] = 0 ;若 s[i] == 'F' ,则 dp[i][i][1] = 0 , dp[i][i][0] = 1 。 计算顺序 :按区间长度 len 从小(1)到大(n)遍历,每次处理固定长度的所有区间。 详细步骤 解析字符串,记录长度 n 。 初始化三维数组 dp[n][n][2] ,全部置0。 处理长度为1的区间(基础情况): 对于每个 i ( 0 <= i < n ,且 i 为偶数,因为布尔值在偶数索引): 若 s[i] == 'T' : dp[i][i][1] = 1 若 s[i] == 'F' : dp[i][i][0] = 1 遍历区间长度 len = 3, 5, 7, ..., n (因为表达式长度必为奇数): 遍历区间起点 i ,使得 i + len - 1 < n : 设区间终点 j = i + len - 1 。 遍历区间内的运算符位置 k ( k 从 i+1 到 j-1 ,步长为2): 左子区间 [i, k-1] ,右子区间 [k+1, j] 。 根据 s[k] 的运算符类型,计算左右子区间组合后的 true_count 和 false_count : 若 s[k] == '&' : true_count = dp[i][k-1][1] * dp[k+1][j][1] false_count = (dp[i][k-1][0] + dp[i][k-1][1]) * (dp[k+1][j][0] + dp[k+1][j][1]) - true_count 若 s[k] == '|' : left_total = dp[i][k-1][0] + dp[i][k-1][1] right_total = dp[k+1][j][0] + dp[k+1][j][1] true_count = dp[i][k-1][1] * right_total + left_total * dp[k+1][j][1] - dp[i][k-1][1] * dp[k+1][j][1] false_count = left_total * right_total - true_count 若 s[k] == '^' : true_count = dp[i][k-1][1] * dp[k+1][j][0] + dp[i][k-1][0] * dp[k+1][j][1] false_count = (dp[i][k-1][0] + dp[i][k-1][1]) * (dp[k+1][j][0] + dp[k+1][j][1]) - true_count 将 true_count 和 false_count 累加到 dp[i][j][1] 和 dp[i][j][0] 。 最终结果为 dp[0][n-1][1] 。 示例验证 以表达式 "T|F" 为例( n=3 ): 初始化: dp[0][0][1]=1 ( T ), dp[2][2][0]=1 ( F )。 区间 [0,2] ,运算符 k=1 为 | : left_total = 1 , right_total = 1 true_count = 1*1 + 1*1 - 1*1 = 1 dp[0][2][1] = 1 结果有1种方式得到 True ,符合预期( T|F 为真)。