括号插入的最小成本问题
字数 1457 2025-11-22 12:58:40

括号插入的最小成本问题

我将为您详细讲解括号插入的最小成本问题,这是一个经典的区间动态规划问题。

问题描述

给定一个布尔表达式字符串,其中包含:

  • 布尔值:'T' (true) 和 'F' (false)
  • 逻辑运算符:'&' (AND)、'|' (OR)、'^' (XOR)

我们需要通过插入括号来改变运算顺序,使得整个表达式的结果为指定值(true或false),并且插入括号的成本最小。每个括号对的插入成本为1。

示例:

表达式: "T|F&T^F"
目标结果: true

我们需要找到使表达式结果为true的最小括号插入成本。

解题思路

步骤1:问题分析

这是一个典型的区间DP问题,因为:

  1. 表达式可以划分为子表达式
  2. 子表达式的结果依赖于运算符和操作数
  3. 括号插入会影响运算顺序,从而影响最终结果

步骤2:状态定义

我们定义三维DP数组:

  • dp[i][j][0]:表示子表达式 s[i...j] 结果为false的最小成本
  • dp[i][j][1]:表示子表达式 s[i...j] 结果为true的最小成本

其中 ij 是字符串的索引。

步骤3:基础情况

对于长度为1的子表达式(单个布尔值):

  • 如果是 'T':dp[i][i][1] = 0dp[i][i][0] = ∞(不可能为false)
  • 如果是 'F':dp[i][i][0] = 0dp[i][i][1] = ∞(不可能为true)

步骤4:状态转移方程

对于区间 [i, j],我们考虑在位置 k(运算符位置)进行分割:

dp[i][j][result] = min(
    dp[i][j][result],
    dp[i][k-1][left] + dp[k+1][j][right] + cost
)

其中:

  • k 是运算符位置(i < k < j,且 k 为奇数索引)
  • leftright 是左右子表达式的结果
  • result 根据运算符和左右结果计算得出
  • cost 是插入括号的成本(通常为1)

步骤5:运算符真值表

我们需要根据不同的运算符来确定结果:

AND运算符 (&):

  • true & true = true
  • true & false = false
  • false & true = false
  • false & false = false

OR运算符 (|):

  • true | true = true
  • true | false = true
  • false | true = true
  • false | false = false

XOR运算符 (^):

  • true ^ true = false
  • true ^ false = true
  • false ^ true = true
  • false ^ false = false

步骤6:算法实现

def min_cost_to_achieve_target(expression, target):
    n = len(expression)
    
    # 初始化DP数组
    dp_true = [[float('inf')] * n for _ in range(n)]
    dp_false = [[float('inf')] * n for _ in range(n)]
    
    # 基础情况:单个布尔值
    for i in range(0, n, 2):
        if expression[i] == 'T':
            dp_true[i][i] = 0
            dp_false[i][i] = float('inf')
        else:  # 'F'
            dp_true[i][i] = float('inf')
            dp_false[i][i] = 0
    
    # 区间长度从3开始(操作数+运算符+操作数)
    for length in range(3, n + 1, 2):
        for i in range(0, n - length + 1, 2):
            j = i + length - 1
            
            # 遍历所有可能的运算符分割点
            for k in range(i + 1, j, 2):
                op = expression[k]
                left_true = dp_true[i][k-1]
                left_false = dp_false[i][k-1]
                right_true = dp_true[k+1][j]
                right_false = dp_false[k+1][j]
                
                # 根据运算符计算结果
                if op == '&':
                    # true = left_true AND right_true
                    dp_true[i][j] = min(dp_true[i][j], left_true + right_true)
                    # false = 其他三种情况
                    dp_false[i][j] = min(dp_false[i][j], 
                                        left_true + right_false,
                                        left_false + right_true, 
                                        left_false + right_false)
                
                elif op == '|':
                    # true = 除了false|false的其他情况
                    dp_true[i][j] = min(dp_true[i][j],
                                        left_true + right_true,
                                        left_true + right_false,
                                        left_false + right_true)
                    # false = left_false OR right_false
                    dp_false[i][j] = min(dp_false[i][j], left_false + right_false)
                
                elif op == '^':
                    # true = left_true XOR right_false 或 left_false XOR right_true
                    dp_true[i][j] = min(dp_true[i][j],
                                        left_true + right_false,
                                        left_false + right_true)
                    # false = left_true XOR right_true 或 left_false XOR right_false
                    dp_false[i][j] = min(dp_false[i][j],
                                        left_true + right_true,
                                        left_false + right_false)
    
    # 返回目标结果的最小成本
    if target:
        return dp_true[0][n-1] if dp_true[0][n-1] != float('inf') else -1
    else:
        return dp_false[0][n-1] if dp_false[0][n-1] != float('inf') else -1

步骤7:复杂度分析

  • 时间复杂度:O(n³),其中n是表达式长度
    • 外层循环:O(n) 个区间长度
    • 中层循环:O(n) 个区间起点
    • 内层循环:O(n) 个分割点
  • 空间复杂度:O(n²),用于存储DP表

步骤8:示例演示

以表达式 "T|F&T^F" 目标为true为例:

  1. 基础情况:

    • dp[0][0][T]=0, dp[0][0][F]=∞
    • dp[2][2][T]=∞, dp[2][2][F]=0
    • dp[4][4][T]=0, dp[4][4][F]=∞
    • dp[6][6][T]=∞, dp[6][6][F]=0
  2. 逐步计算:

    • 先计算长度为3的区间
    • 再计算长度为5的区间
    • 最后计算整个表达式

最终找到最小成本方案。

这个算法能够有效地找到使布尔表达式达到目标值的最小括号插入成本,是区间动态规划的经典应用。

括号插入的最小成本问题 我将为您详细讲解括号插入的最小成本问题,这是一个经典的区间动态规划问题。 问题描述 给定一个布尔表达式字符串,其中包含: 布尔值:'T' (true) 和 'F' (false) 逻辑运算符:'&' (AND)、'|' (OR)、'^' (XOR) 我们需要通过插入括号来改变运算顺序,使得整个表达式的结果为指定值(true或false),并且插入括号的成本最小。每个括号对的插入成本为1。 示例: 我们需要找到使表达式结果为true的最小括号插入成本。 解题思路 步骤1:问题分析 这是一个典型的区间DP问题,因为: 表达式可以划分为子表达式 子表达式的结果依赖于运算符和操作数 括号插入会影响运算顺序,从而影响最终结果 步骤2:状态定义 我们定义三维DP数组: dp[i][j][0] :表示子表达式 s[i...j] 结果为false的最小成本 dp[i][j][1] :表示子表达式 s[i...j] 结果为true的最小成本 其中 i 和 j 是字符串的索引。 步骤3:基础情况 对于长度为1的子表达式(单个布尔值): 如果是 'T': dp[i][i][1] = 0 , dp[i][i][0] = ∞ (不可能为false) 如果是 'F': dp[i][i][0] = 0 , dp[i][i][1] = ∞ (不可能为true) 步骤4:状态转移方程 对于区间 [i, j] ,我们考虑在位置 k (运算符位置)进行分割: 其中: k 是运算符位置( i < k < j ,且 k 为奇数索引) left 和 right 是左右子表达式的结果 result 根据运算符和左右结果计算得出 cost 是插入括号的成本(通常为1) 步骤5:运算符真值表 我们需要根据不同的运算符来确定结果: AND运算符 (&): true & true = true true & false = false false & true = false false & false = false OR运算符 (|): true | true = true true | false = true false | true = true false | false = false XOR运算符 (^): true ^ true = false true ^ false = true false ^ true = true false ^ false = false 步骤6:算法实现 步骤7:复杂度分析 时间复杂度 :O(n³),其中n是表达式长度 外层循环:O(n) 个区间长度 中层循环:O(n) 个区间起点 内层循环:O(n) 个分割点 空间复杂度 :O(n²),用于存储DP表 步骤8:示例演示 以表达式 "T|F&T^F" 目标为true为例: 基础情况: dp[ 0][ 0][ T]=0, dp[ 0][ 0][ F ]=∞ dp[ 2][ 2][ T]=∞, dp[ 2][ 2][ F ]=0 dp[ 4][ 4][ T]=0, dp[ 4][ 4][ F ]=∞ dp[ 6][ 6][ T]=∞, dp[ 6][ 6][ F ]=0 逐步计算: 先计算长度为3的区间 再计算长度为5的区间 最后计算整个表达式 最终找到最小成本方案。 这个算法能够有效地找到使布尔表达式达到目标值的最小括号插入成本,是区间动态规划的经典应用。