UVa 10454 Trexpression

题目分析

本题要求计算一个代数表达式的所有可能的树表示数量。代数表达式由单数字、加号(+)、乘号(*)和括号组成,需要满足以下条件:

  1. 操作数顺序不变:在所有的树表示中,操作数的相对顺序必须与原始表达式一致
  2. 运算符优先级保持:乘法的优先级高于加法,除非括号改变了运算顺序
  3. 括号强制分组:括号内的表达式必须作为一个整体处理

关键观察

从题目给出的样例可以看出:

  • 1+2+3+4 输出 555(所有可能的二叉树结构)
  • (1+2)+(3+4) 输出 111(括号强制分组,只有一种结构)
  • 1+2+3*4 输出 222(乘法优先级限制,只有两种有效结构)
  • 1+2+(3*4) 输出 222(括号保护乘法,有两种有效结构)

解题思路

核心思想

使用动态规划结合深度优先搜索来解决问题。对于表达式区间 [l,r][l, r][l,r],我们枚举所有可能的分裂点,将表达式分为左右两个子表达式,然后递归计算。

优先级处理的关键洞察

  1. 加法运算符 (+):总是可以作为分裂点,因为加法优先级最低
  2. 乘法运算符 (*):只有当整个区间没有加法运算符时才能作为分裂点
  3. 括号处理:跳过括号内的运算符,只考虑当前层级的运算符

算法步骤

  1. 基本情况:当区间长度为 111 时(单个操作数),只有一种树表示
  2. 括号检查:如果整个表达式被括号包围且没有外层运算符,去掉括号递归计算
  3. 运算符扫描:找出当前层级(括号外)的所有运算符类型
  4. 分裂枚举
    • 如果有 + 运算符,只能按 + 分裂
    • 如果没有 + 只有 *,可以按 * 分裂
    • 计算左右子树数量的乘积并累加

复杂度分析

  • 时间复杂度O(n3)O(n^3)O(n3),其中 nnn 是表达式长度
  • 空间复杂度O(n2)O(n^2)O(n2),用于存储动态规划状态

代码实现

// Trexpression
// UVa ID: 10454
// Verdict: Accepted
// Submission Date: 2025-11-25
// UVa Run Time: 0.300s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 256;
long long dp[MAXN][MAXN];
string expression;

long long dfs(int l, int r) {
    if (l > r) return 1;
    if (l == r) return 1;
    if (dp[l][r] != -1) return dp[l][r];
    
    long long &result = dp[l][r];
    result = 0;
    
    int left = 0, hasPlus = 0, hasMultiply = 0;
    
    // 扫描当前区间,找出括号外的运算符
    for (int i = l; i <= r; i++) {
        if (expression[i] == '(') left++;
        else if (expression[i] == ')') left--;
        else {
            if (left == 0) {
                if (expression[i] == '+') hasPlus = 1;
                if (expression[i] == '*') hasMultiply = 1;
            }
        }
    }
    
    // 如果整个表达式被括号包围且没有外层运算符,去掉括号
    if (hasPlus == 0 && hasMultiply == 0 && expression[l] == '(' && expression[r] == ')') 
        result += dfs(l + 1, r - 1);
    
    // 枚举分裂点
    left = 0;
    for (int i = l; i <= r; i++) {
        if (expression[i] == '(') left++;
        else if (expression[i] == ')') left--;
        else {
            if (left == 0) {
                if (expression[i] == '+') 
                    result += dfs(l, i - 1) * dfs(i + 1, r);
                if (expression[i] == '*' && hasPlus == 0) 
                    result += dfs(l, i - 1) * dfs(i + 1, r);
            }
        }
    }
    
    return result;
}

int main() {
    while (getline(cin, expression)) {
        memset(dp, -1, sizeof(dp));
        long long result = dfs(0, expression.length() - 1);
        cout << result << endl;
    }
    return 0;
}

总结

本题的关键在于理解运算符优先级对树结构选择的限制。通过巧妙的括号处理和运算符类型判断,避免了复杂的优先级计算,使得算法既高效又易于实现。这种方法体现了在解决表达式解析问题时,有时可以通过简单的规则替代复杂的计算,达到事半功倍的效果。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值