当前位置:首页 > 牛客题解 > 牛客4469题解:布尔表达式方案数的动态规划解法

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

12小时前牛客题解25

截图未命名.jpg 牛客4469题解:布尔表达式方案数的动态规划解法 动态规划 布尔表达式 运算符优先级 模运算优化 区间动态规划 牛客题解 第1张

一、问题理解

牛客4469题要求计算给定布尔表达式(仅含0,1和&|^运算符)通过不同运算顺序得到指定结果(0或1)的方案数。这是一个典型的区间动态规划问题,在编译器优化、逻辑电路设计等领域有重要应用。

二、算法核心思想

使用三维动态规划数组dp[i][j][r]表示区间i到j计算结果为r的方案数:

  1. 将表达式分离为操作数数组和运算符数组

  2. 枚举所有可能的区间分割点

  3. 根据运算符类型组合左右子区间的结果

三、完整代码实现(带注释)

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;
        }
    }
};

四、关键点解析

  1. 数据结构分离

    • nums数组存储操作数(0/1)

    • ops数组存储运算符(&|^)

  2. DP数组初始化

    • 单个数字的区间直接初始化为1

    • 三维数组第三维表示0/1两种结果

  3. 区间DP过程

    • 外层循环枚举区间长度

    • 中层循环枚举区间起点

    • 内层循环枚举分割点

  4. 运算符处理

    • 通过compute函数统一处理三种运算符

    • 模运算防止结果溢出

五、常见问题解答

Q: 为什么使用MOD 10007? A: 题目要求结果对10007取模,防止大数溢出

Q: 时间复杂度是多少? A: O(n^3),三重循环分别对应区间长度、起点和分割点

Q: 如何处理运算符优先级? A: 本题不考虑优先级,所有运算顺序都可能


原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。