P1812 区间运算 题解
题意简析
给定若干个由区间组成的中缀表达式,求出它们的值。
区间运算规则如下:
- 取相反数:\[-[a,b]=[-b,-a] \]
- 加法:\[[a,b]+[c,d]=[a+c,b+d] \]
- 减法:\[[a,b]-[c,d]=[a-d,b-c] \]
- 乘法:
\[[a,b] * [c,d]=\min(ac,ad,bc,bd),\max(ac,ad,bc,bd)]
\]
- 除法:
\[[a,b] \div [c,d]=[\min(\frac{a}{c},\frac{a}{d},\frac{b}{c},\frac{b}{d}),\max(\frac{a}{c},\frac{a}{d},\frac{b}{c},\frac{b}{d})]
\]
且 \(c,d \neq 0\)。
思路解析
根据题意,我们很容易写出以下代码:
namespace RANGE {
const long double Infinity_Range = 1e18;
const long double _Infinity_Range = -1e18;
struct Range {
long double l, r;
Range operator+(const Range& b) const {
return Range{l + b.l, r + b.r};
}
Range operator-(const Range& b) const {
return Range{l - b.r, r - b.l};
}
Range operator*(const Range& b) const {
return Range{min(l * b.l, l * b.r, r * b.l, r * b.r), max(l * b.l, l * b.r, r * b.l, r * b.r)};
}
Range operator/(const Range& b) const {
if (!b.l || !b.r) {
return Range{_Infinity_Range, Infinity_Range};
}
return Range{max(l / b.l, l / b.r, r / b.l, r / b.r), min(l / b.l, l / b.r, r / b.l, r / b.r)};
}
Range operator!() const {
return Range{-r, -l};
}
};
} // namespace RANGE
using namespace RANGE;
但是我们仅仅掌握了它的运算规则,那到底从哪着手运算呢?那么多括号该怎么处理呢?
表达式求值

如图,这是一棵表达式 \((a-b)*c\) 的表达式树,可以发现,叶子节点存储数值,非叶子节点存储运算符号。
前缀表达式(波兰式)
波兰表示法,是波兰数学家、逻辑学家、哲学家扬·武卡谢维奇,于 20 世纪 20 年代,提出的不需用括号表示一个式子的方法,简洁明了,在计算机科学中有广泛应用。
是表达式树的前序遍历。
例子中的前缀表达式(波兰式)即为:
\[*-abc
\]
中缀表达式
是表达式树的中序遍历,也是我们最常用的。有时候需要添加括号来表示运算的先后。

例子中的中缀表达式即为:
\[(a-b)*c
\]
后缀表达式(逆波兰式)
逆波兰表示法,是波兰数学家、逻辑学家、哲学家扬·武卡谢维奇,于 20 世纪 20 年代,提出的不需用括号表示一个式子的方法,简洁明了,在计算机科学中有广泛应用。(
没错又是他)
是表达式树的后序遍历。
例子中的后缀表达式(逆波兰式)即为:
\[ab-c*
\]
样例分析
前面讲了这么多和这道题有什么关系呢?
-[-3,5]
[3,5]+[-10,1]
[3,5]-[-10,1]
[3,5]*[-10,1]
(([3,5]/[-10,-0.1])/-[2,2])
样例中的四个表达式可以画作四颗表达式树:





我们在学习树的遍历的时候,可以用递归来解决,那么我们求类似的问题时,可不可以也用递归实现呢?显然是可以的。
更加深入地,我们想到递归也可以用栈来表示,边存边算。具体地:
- 若读入的为左括号,将新元素压入栈,深度增大。
- 若读入的为右括号,计算结果,把计算过的元素出栈,把结果入栈,深度减少。
- 若读入的运算符号运算优先级小于等于栈顶的运算符号,那么这时候就要从左往右正常运算,把计算过的元素出栈,把结果入栈,深度减少。
代码实现
对于出现除数区间为 \(0\) 的,我们可以把左右区间变成无穷大特判,也可以使用异常捕获语句。
Python
eval() 是 Python 中的一个强大的函数,它可以将字符串作为代码执行。
定义一个 Interval 类,并重载运算符来实现区间运算。
class Interval:
def __init__(self, min_val, max_val):
# 确保 min_val <= max_val
self.min = min(min_val, max_val)
self.max = max(min_val, max_val)
def __neg__(self):
return Interval(-self.max, -self.min)
def __add__(self, other):
return Interval(self.min + other.min, self.max + other.max)
def __sub__(self, other):
return Interval(self.min - other.max, self.max - other.min)
def __mul__(self, other):
candidates = [
self.min * other.min,
self.min * other.max,
self.max * other.min,
self.max * other.max
]
return Interval(min(candidates), max(candidates))
def __truediv__(self, other):
if other.min <= 0 <= other.max:
raise ZeroDivisionError("Division by zero")
candidates = [
self.min / other.min if other.min != 0 else float('inf'),
self.min / other.max if other.max != 0 else float('inf'),
self.max / other.min if other.min != 0 else float('inf'),
self.max / other.max if other.max != 0 else float('inf')
]
return Interval(min(candidates), max(candidates))
def __repr__(self):
return f"[{self.min:.3f},{self.max:.3f}]"
def parse_interval(s):
min_val, max_val = map(float, s.strip("[]").split(","))
# 确保 min_val <= max_val
return Interval(min_val, max_val)
def safe_eval(expression):
# 替换区间表示为 Interval 对象
while "[" in expression:
start = expression.find("[")
end = expression.find("]")
interval_str = expression[start:end + 1]
interval_obj = parse_interval(interval_str)
expression = expression.replace(interval_str, f"Interval({interval_obj.min}, {interval_obj.max})", 1)
return eval(expression, {"__builtins__": None}, {"Interval": Interval})
def main():
import sys
input = sys.stdin.read
data = input().strip().split("\n")
for line in data:
try:
result = safe_eval(line)
print(result)
except ZeroDivisionError:
print("Division by zero")
if __name__ == "__main__":
main()
后记
- 除法中如果除数的区间包含 \(0\),则输出
Division by zero。 - \(l > r\) 时要交换 \(l\) 和 \(r\)!
- 考虑 \(0.0000\) 和 \(-0.0000\) 的特殊情况。

浙公网安备 33010602011771号