牛客4469题解:布尔表达式方案数的动态规划解法

一、问题理解
牛客4469题要求计算给定布尔表达式(仅含0,1和&|^运算符)通过不同运算顺序得到指定结果(0或1)的方案数。这是一个典型的区间动态规划问题,在编译器优化、逻辑电路设计等领域有重要应用。
二、算法核心思想
使用三维动态规划数组DP[i][j][r]表示区间i到j计算结果为r的方案数:
将表达式分离为操作数数组和运算符数组
枚举所有可能的区间分割点
根据运算符类型组合左右子区间的结果
三、完整代码实现(带注释)
class Expression {
public:
const int MOD = 10007;
int countWays(string exp, int len, int ret) {
// 分离数字和运算符
vector<int> nums;
vector<char> ops;
for (int i = 0; i < len; ) {
if (exp[i] == '0' || exp[i] == '1') {
nums.push_back(exp[i] - '0');
i++;
} else {
ops.push_back(exp[i]);
i++;
}
}
int n = nums.size();
if (n == 0) return 0;
// dp[i][j][0/1]表示区间i到j结果为0/1的方案数
vector<vector<vector<int>>> dp(n,
vector<vector<int>>(n, vector<int>(2, 0)));
// 初始化单个数字的情况
for (int i = 0; i < n; i++) {
dp[i][i][nums[i]] = 1;
}
// 区间DP
for (int l = 2; l <= n; l++) { // 区间长度
for (int i = 0; i + l - 1 < n; i++) { // 区间起点
int j = i + l - 1; // 区间终点
for (int k = i; k < j; k++) { // 分割点
char op = ops[k];
// 计算左右子区间的组合
for (int left = 0; left <= 1; left++) {
for (int right = 0; right <= 1; right++) {
int res = compute(left, right, op);
dp[i][j][res] = (dp[i][j][res] +
dp[i][k][left] * dp[k + 1][j][right]) % MOD;
}
}
}
}
}
return dp[0][n - 1][ret];
}
int compute(int a, int b, char op) {
switch (op) {
case '&':
return a & b;
case '|':
return a | b;
case '^':
return a ^ b;
default:
return 0;
}
}
};四、关键点解析
数据结构分离:
nums数组存储操作数(0/1)
ops数组存储运算符(&|^)
DP数组初始化:
单个数字的区间直接初始化为1
三维数组第三维表示0/1两种结果
区间DP过程:
外层循环枚举区间长度
中层循环枚举区间起点
内层循环枚举分割点
运算符处理:
通过compute函数统一处理三种运算符
模运算防止结果溢出
五、常见问题解答
Q: 为什么使用MOD 10007? A: 题目要求结果对10007取模,防止大数溢出
Q: 时间复杂度是多少? A: O(n^3),三重循环分别对应区间长度、起点和分割点
Q: 如何处理运算符优先级? A: 本题不考虑优先级,所有运算顺序都可能
原创内容 转载请注明出处
