leetcode.241 以此为例整理动态规划,包含步骤和模拟例子

  1. 思路
  2. 解析表达式
  3. 初始化动态规划列表
  4. 填充动态规划表
  5. 获取最终结果
  6. 示例
  7. Code

思路

CODE和官方解析一样,主要借此地方整理一下动态规划,并用一个具体例子,捋顺思路。

动态规划用于解决具有重叠子问题 和最优子结构特性的问题。

一这个题为例,我们希望找到给定算术表达式的所有可能计算结果,这个问题可以分为一系列子问题,在这个题里,子问题就是————计算表达式不同部分得到的解

首先我们达到计算字符串,形式的算术表达式。有运算符有数字,我们通过添加括号,改变运算优先顺序,然后得到所有可能的结果。

动态规划算法可以归为以下步骤,对照Code,可以被划分为四个部分。

解析表达式

对标一开始的初始化,要注意,数字,可能不止一位数字,所以循环套循环。
值得注意的一点:
在这道题 奇数索引位置代表运算符 ;偶数索引位置代表数字。

初始化动态规划列表

我们使用一个二维向量 dp 来存储结果,其中 dp[i][j] 存储子表达式 ops[i] 到 ops[j] 所有可能的计算结果。

填充动态规划表

动态规划过程的核心是填充这个 dp 表。我们从最小的子表达式开始(只包含单个数字),然后逐渐增加子表达式的大小。

最小子表达式:对于单个数字的子表达式(不包含任何运算符),dp[i][i] 只有一个可能的结果,即它自己。

增加子表达式大小:当子表达式的长度增加时,我们将其分成两部分:左边的子表达式和右边的子表达式。我们在可能的每个位置 k 分割子表达式,k 是位于 l 和 r 之间的任何运算符的位置。
对于每个分割点 k,我们结合 dp[l][k-1](左边所有可能的结果)和 dp[k+1][r](右边所有可能的结果)以及 ops[k](分割点处的运算符)来生成新的结果,并将这些结果添加到 dp[l][r]。

获取最终结果

动态规划过程完成后,dp[0][ops.size() - 1] 将包含整个表达式的所有可能结果。

示例

以表达式 2 * 3-4 * 5 为例:

解析表达式后,我们得到序列 [2, '', 3, '-', 4, '', 5]。
初始化 dp,每个数字自己就是一个结果,所以 dp[i][i] 就是 [2], [3], [4], [5]。
而且这地方应该是:

dp[0][0] = [2]       // 只有一个数字2
dp[1][1] = []        // 位置1是运算符,没有单独的计算结果
dp[2][2] = [3]       // 只有一个数字3
。。。。
dp[4][4] = [4]    // 只有一个数字 4
dp[6][6] = [5]    // 只有一个数字 5

我们开始填充 dp,首先是长度为 3 的子表达式,所以从i = 3 开始,比如 2 * 3, 3 - 4, 4 * 5。
2*3,只有一个计算方法。

dp[0][2] = [6] //ops的位置[0, 2]
dp[2][4] = [-1] // ops的位置[2, 4]
dp[4][6] = [20] //ops的位置[4, 6]

然后是长度为 5 的子表达式,比如 2 * 3 - 4, 3- 4 * 5,直到我们计算整个表达式 2 * 3- 4 * 5。

dp[0][4] = [-2,2]//ops的位置[0, 4], dp[0][2] - dp[4][4] 的组合,[0][0]  * [2][4]
dp[2][6] = [-17,-5]//ops的位置[2, 6], dp[2][2] - dp[4][6] 的组合 24 * 66

最后

dp[0,6] = [-34,-10,-14,-10, 10]  //00*26 02-46 04*66

在动态规划的每一步,我们都在考虑不同的分割点,查看如果在这个点添加括号会如何影响计算结果,然后将所有的结果合并。

通过这种方式,我们能够高效地计算出所有可能的结果,因为每个子表达式的结果都是基于更小子表达式结果的组合,避免了重复计算。

Code

C++


class Solution {
public:
    const int ADD = -1;
    const int SUB = -2;
    const int MULTI = -3;

    vector<int> diffWaysToCompute(string expression) {
        vector<int>ops;
        // initialization 最初初始化
        //将输入字符串expression解析为一个队列ops,其中包含数字和运算符(这里运算符用特殊的整数值表示)
        for(int i = 0; i<expression.size();){
            //运算符
            if(!isdigit(expression[i])){
                if(expression[i]=='+'){ops.push_back(ADD);}
                else if(expression[i] == '-'){ops.push_back(SUB);}
                else if(expression[i] == '*'){ops.push_back(MULTI);}
                i++;
            }else{
                int t = 0;
                // 因为是string

                while(i < expression.size() && isdigit(expression[i])){
                    t = t*10 + expression[i] - '0';
                    i++;
                }
                ops.push_back(t);
                //如果expression是“123+456”,那么代码首先会解析出123并将其添加到ops,然后处理`+号,在遇到 456 时重复的步骤。最终ops将包含之前[123, ADDITION, 456],其中ADDITION是一个特殊的值
                // 所以expression数字和运算符交替存在
            }
            
        }

// dp 初始化动态规划列表
//         初始化规划动态存储dp,其数量是n x n,n是ops存储的容量。
        vector<vector<vector<int>>> dp((int)ops.size(),vector<vector<int>>((int)ops.size()));
// 最小子表达式
        for(int i = 0; i < ops.size(); i+=2){
            dp[i][i] = {ops[i]};
        }
//增加子表达式大小
        for(int i =3;i<=ops.size();i++){
            for(int j = 0; j+i <=ops.size();j+=2){
                int l = j;
                int r = j+i-1;
                for(int k = j + 1; k < r; k+=2){
                    auto left = dp[l][k-1];
                    auto right = dp[k+1][r];
                    for (auto& num1:left){
                        for(auto& num2:right){
                            if(ops[k] == ADD){
                                dp[l][r].push_back(num1+num2);
                            }else if(ops[k] == SUB){
                                dp[l][r].push_back(num1 - num2);
                            }else if(ops[k] == MULTI){
                                dp[l][r].push_back(num1 * num2);
                            }
                        }
                    }
                }
            }
        }
// 得出最终结论
         return dp[0][(int) ops.size() - 1];

 
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值