洛谷P1310 表达式的值 题解 栈/后缀表达式的应用

时间:2024-10-17 23:06:44

题目链接:https://www.luogu.org/problem/P1310

本题涉及算法:栈、前缀表达式转后缀表达式,动态规划思想。

这道题目我思考了好长时间,第一时间让我做的话我也做不出来。

看洛谷上面的题解给了我很大的启发。

其中最重要的两点启发是:

启发1:中缀加操作数预处理

将原始表达式中添加上‘.’,这些‘.’对应运算数(这么预处理能方便我接下来更方便地将前缀转后缀表达式进行处理);

启发2:动归思想

首先一个状态对应两个值,我暂且将它们设为 \(v0\) 和 \(v1\) ,其中:

  • \(v0\) 表示该状态下结果为 \(0\) 的方案数;
  • \(v1\) 表示该状态下结果为 \(1\) 的方案数。

那么,在前缀转中缀的时候,只有我们假设由两个状态 \(a\) 和 \(b\) 变换到新的状态 \(c\),那么:

  • 如果进行的是 + 操作,则 \(c.v0 = a.v0 \times b.v0\) ,\(c.v1 = a.v0 \times b.v1 + a.v1 \times b.v0 + a.v1 \times b.v1\) ;
  • 如果进行的是 * 操作,则 \(c.v0 = a.v0 \times b.v0 + a.v0 \times b.v1 + a.v1 \times b.v0\) ,\(c.v1 = a.v1 \times b.v1\) 。

这里和原始的前缀转后缀的区别是:

  • 原始进栈的都是一个个单独的操作数;
  • 这里进行的都是一个个出事状态 \(p\) ,这些 \(p\) 满足 \(p.v0 = p.v1 = 1\)(即:单独一个数的时候结果为 \(0\) 或者为 \(1\) 的方案数都为 \(1\))。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200200;
const int MOD = 10007;
char s[maxn], t[maxn];
int m, n;
pair<int, int> num_stk[maxn];
char flag_stk[maxn];
int num_top, flag_top;
int main() {
scanf("%d%s", &m, s);
if (s[0] == '+' || s[0] == '*') t[n++] = '.';
for (int i = 0; s[i]; i ++) {
t[n++] = s[i];
if ( (s[i] == '+' || s[i] == '*' || s[i] == '(') && (!s[i+1] || s[i+1] != '(') )
t[n++] = '.';
}
for (int i = 0; i < n; i ++) {
if (t[i] == '.') {
num_stk[++ num_top] = make_pair(1, 1);
while (flag_top > 0 && flag_stk[flag_top] == '*') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
( p1.first * p2.first + p1.first * p2.second + p1.second * p2.first ) % MOD,
p1.second * p2.second % MOD
);
}
}
else if (t[i] == '+') {
while (flag_top > 0 && flag_stk[flag_top] == '*') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
( p1.first * p2.first + p1.first * p2.second + p1.second * p2.first ) % MOD,
p1.second * p2.second % MOD
);
}
while (flag_top > 0 && flag_stk[flag_top] == '+') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
p1.first * p2.first % MOD,
( p1.first * p2.second + p1.second * p2.first + p1.second * p2.second ) % MOD
);
}
flag_stk[++ flag_top] = '+';
}
else if (t[i] == '(' || t[i] == '*') {
flag_stk[++ flag_top] = t[i];
}
else if (t[i] == ')') {
while (flag_top > 0 && flag_stk[flag_top] != '(') {
if (flag_stk[flag_top] == '*') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
( p1.first * p2.first + p1.first * p2.second + p1.second * p2.first ) % MOD,
p1.second * p2.second % MOD
);
}
else if (flag_stk[flag_top] == '+') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
p1.first * p2.first % MOD,
( p1.first * p2.second + p1.second * p2.first + p1.second * p2.second ) % MOD
);
} }
flag_top --; // remove '('
}
}
while (flag_top > 0) {
if (flag_stk[flag_top] == '*') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
( p1.first * p2.first + p1.first * p2.second + p1.second * p2.first ) % MOD,
p1.second * p2.second % MOD
);
}
else if (flag_stk[flag_top] == '+') {
flag_top --;
pair<int, int> p1 = num_stk[num_top --];
pair<int, int> p2 = num_stk[num_top --];
num_stk[++ num_top] = make_pair(
p1.first * p2.first % MOD,
( p1.first * p2.second + p1.second * p2.first + p1.second * p2.second ) % MOD
);
}
}
printf("%d\n", num_stk[1].first);
return 0;
}

这里虽然我过了代码,但是我觉得我对后缀表达式的理解还没有达到那种深度。所以后续还是需要进一步理解如果方便快捷地进行前缀到后缀表达式的转换。

作者:zifeiy