区间动态规划例题:布尔括号化问题
字数 2544 2025-10-26 19:15:23
区间动态规划例题:布尔括号化问题
题目描述
给定一个布尔表达式,由符号 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 = 1true_count = 1*1 + 1*1 - 1*1 = 1dp[0][2][1] = 1
结果有1种方式得到True,符合预期(T|F为真)。