【t094】区间运算
Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
区间运算是数学的一个领域。在区间运算中,常量和变量并不表示为一个单独、精确的值,而是表示为一个有着上界和下界的区间
或范围。在普通的运算中,一个数量可以表示为数轴上的一个点;而在区间运算中,一个数量表示数轴上的一段,例如[3,5]表
示数轴上从3到5的一段。当精确的数值表示为区间时,上界与下界是相同的,如5表示为区间即[5,5]。
两个区间的运算,是指一个区间中的每个点与另一个区间中的每个点所做的运算,通过运算所得的所有点的集合即为运算的结
果。例如,[3,5]+[-10,1]=[-7,6]。你的任务是写一个可以根据单行表达式进行取反数、加、减、乘、除等基本的区间运算的
程序。下面是一些运算的例子:
取相反数 -[-3,5]=[-5,3]
加法 [3,5]+[-10,1]=[-7,6]
减法 [3,5]-[-10,1]=[2,15]
乘法 [3,5]*[-10,1]=[-50,5]
除法 [3,5]/[-10,-0.1]=[-50,-0.3]
【输入格式】
程序的输入包含一行或多行区间运算的中缀表达式,每个区间都表示为[min,max],表 达式中包括括号、负号(-)、加号(+)、减号
(-)、乘号(*)和除号(/),括号可能是嵌套的。每一行中都有可能有空格,但空格不会在表示区间的括号“[min,max]”中间或
负号后出现。程序不需要处理科学记数法(如2E6=2000000)。每一行均不超过 80 个字符。运算采用标准的优先级规则。下面按
优先级递减的顺序给出各个运算符:
() 括号
- 取相反数
* / 乘法和除法,同级运算按从左往右的顺序
+ - 加法和减法,同级运算按从左往右的顺序
【输出格式】
对于输入的每一行,都相应地输出一行,输出的内容就是区间运算的最后结果,用[min,max]的形式表示,其中min不能大于max,
输出结果都精确到小数点后第三位,区间形式中间不能有空格。但如果表达式里面有除法,而且作为除数的区间包含0,则输出
“Division by zero”即可。
Sample Input
-[-3,5]
[3,5]+[-10,1]
[3,5]-[-10,1]
[3,5]*[-10,1]
(([3,5]/[-10,-0.1])/-[2,2])
Sample Output
[-5.000,3.000]
[-7.000,6.000]
[2.000,15.000]
[-50.000,5.000]
[0.150,25.000]
【题解】 对于进行的运算。 设a=[x1,y1],b=[x2,y2] a+b = [x1+x2,y1+y2] a-b = [x1-y2,y1-x2]; d1 = x1 m x2,d2 = x1 m y2,d3 = y1 m x2,d4 = y1 m y2; a*b = [min(d1..d4,m=*),max(d1..d4,m=*)]; a/b = [min(d1..d4,m=/),max(d1..d4,m=/)]; 对于取反。一开始的时候先判断出哪些是取反符号(很容易就能想到要怎么判断的。) 然后在取反符号前加"([0,0]",然后在取反符号后面恰当的位置加上")"。就是把取反运算变成一个减法 运算。 然后利用二分的方法来求表达式。 具体的。 先找到一个表达式里面运算的优先级别最小的运算符。 然后递归这个运算符左边的表达式。递归右边的表达式。 如果当前处理的表达式全为区间。则返回这个区间的端点。存在struct结构体中。 这个递归也是一个struct 结构体。所以可以直接返回。 同一优先级别的操作符。往后找。这样可以保证同一优先级。从左到右运算的顺序。 【代码】#include <cstdio>
#include <iostream>
#include <stdlib.h>
#include <string> using namespace std; struct qujian //结构体。存区间的端点
{
double l,r;
}; bool chu0 = false; //用于判断是否除0,因为不能直接结束程序。所以有点麻烦。 void check(string & s) //把多余的空格去掉
{
int l = s.size()-1;
for (int i = 1;i <= l;i++)
if (s[i] == ' ')
s[i] = '$'; //先变成一个标识符
string ss = " ";
for (int i = 1;i <= l;i++) //然后把那些不是标识符的加到ss这个temp变量上
if (s[i]!='$')
ss+=s[i];
s = ss; //最后把temp变量赋值给s ,完成去除空格的任务。
} bool reducebracket(string &s) //去掉两边的括号,如( (..) + (..))
{//则变成(..)+(..) 注意string 要加& 不然s不会改变
int l = s.size()-1; //因为在前面加了一个空格,所以从1开始数起
if (s[1] != '(' || s[l] != ')') //如果最左和最右不是配对的括号则不可能
return false; //返回不要去括号
int b = 0; //因为可能出现(xxx) + (xxx)的情况。(两边是配对的括号,但不要去)
for (int i = 2;i <= l-1;i++) //在2到l-1的范围内看括号是否匹配。
{
if (s[i] == '(')
b++;
if (s[i] == ')')
b--;
if (b < 0)
return false; //如果不匹配则返回不去除。这可以考虑到上述情况
}
s = s.erase(l,1); //去掉两边的括号
s = s.erase(1,1);
return true; //返回可以再尝试去括号
} char cmp(char x,char y) //比较x操作符和y操作符的优先顺序
{
if (x == '&') //如果是初值,则一定更新
return '>';
if (x == '*' || x == '/') //如果是同一级别的要尽量往后(同一级从左到右)
return '>'; //其他的按照小学知识就能知道优先顺序了。
if ( (x == '+' || x == '-') && (y == '+' || y == '-'))
return '>'; //在找的时候会一层层去掉括号,所以不要管括号
return '<';
} int find (string s) //找s里面运算符级别最小的运算符位置
{
char now = '&'; //置初值
int l = s.size()-1;
int i = 1,k;
while (i <= l)
{
if (s[i] == '(') //如果是括号就要跳过。
{
int b = 0;
do
{
if (s[i] == '(')
b++;
if (s[i] == ')')
b--;
i++; //进行括号的匹配
}
while (b!=0);
}
else
if ((s[i] == '*' || s[i] == '/' || s[i] == '+')
||
(s[i] == '-' && i > 1 &&
(s[i-1] ==']' || s[i-1] == ')')
)) //这一大串都是判断这个是不是操作符
{
if (cmp(now,s[i]) == '>') //如果这个操作符更小
{ //则更新
now = s[i];
k = i;
}
i++; //无论如何都要递增i
}
else
i++;
}
return k;
} double best_max(double x,double y,double z,double w) //返回几个数中最大的数
{
double m = x;
if (y > m)
m = y;
if (z > m)
m = z;
if (w > m)
m = w;
return m;
} double best_min(double x,double y,double z,double w) //返回几个数中最小的数
{
double m = x;
if (y < m)
m = y;
if (z < m)
m = z;
if (w < m)
m = w;
return m;
} qujian reduce(string s) //这是递归的主程序
{
qujian temp;
if (chu0) //如果除0了就随便返回一个结构体。
return temp;
if (s == " ") //如果为空则返回0 我们之前有加一个空格在头部
{
temp.l = 0;
temp.r = 0;
return temp;
}
bool judge = false; //判断这个字符串有没有操作符
int l = s.size()-1;
for (int i = 1;i <= l;i++) //如果有操作符就返回true
if (s[i] == '*' || s[i] == '/' || s[i] == '+')
{
judge = true;
break;
}
else //减号的判断要小心出现负数的情况。这会麻烦点。
if (s[i] == '-' && i > 1 && (s[i-1] ==']' ||
s[i-1] == ')'))
{
judge = true;
break;
}
if (!judge) //如果没有出现操作符。则这是一个区间
{
int p1 = s.find('[',0),p2 = s.find(',',0);
string s1 = s.substr(p1+1,p2-p1-1);
int p3 = s.find(']',0);
string s2 = s.substr(p2+1,p3-p2-1); //把这个区间的a,b取出来
temp.l = atof(s1.c_str());
temp.r = atof(s2.c_str()); //转成double类型
if (temp.l > temp.r) //会出现a > b的情况。很恶心。。
{
double te;
te = temp.l;
temp.l = temp.r;
temp.r = te;
}
return temp;
}
bool flag = reducebracket(s); //如果能去除两边多余括号就去除
while (flag)
flag = reducebracket(s);
int k = find(s); //找到运算符的位置。
string sl = s.substr(0,k); //截取运算符的左边和右边
char key = s[k]; //取出操作符
s = s.erase(1,k);//删掉左边,保留空格。所以从1开始
string sr = s; //右边就直接等于删掉后剩余的东西
qujian temp1 = reduce(sl),temp2= reduce(sr);//递归左边和右边
if (chu0) //如果除0了就随便返回个值。(没用的)
return temp1;
qujian temp3;
switch (key) //根据我在题解写的规则进行运算,注意判断除0
{
case '+':
temp.l=temp1.l+temp2.l,temp.r=temp1.r+temp2.r;
break;
case '-':
temp.l=temp1.l-temp2.r,temp.r=temp1.r-temp2.l;
break;
case '*':
{
temp.r = best_max(temp1.l*temp2.l,temp1.l*temp2.r,
temp1.r*temp2.l,temp1.r*temp2.r);
temp.l = best_min(temp1.l*temp2.l,temp1.l*temp2.r,
temp1.r*temp2.l,temp1.r*temp2.r);
}
break;
case '/':
{
if (temp2.l <=0 && temp2.r >=0) //如果除0,则退出这层递归。
{
chu0 = true; //标记除0信息
return temp2;
}
temp.r = best_max(temp1.l/temp2.l,temp1.l/temp2.r,
temp1.r/temp2.l,temp1.r/temp2.r);
temp.l = best_min(temp1.l/temp2.l,temp1.l/temp2.r,
temp1.r/temp2.l,temp1.r/temp2.r);
}
break;
}
return temp;
} void input_data()
{
string ss;
while ( getline(cin,ss)) //有多行输入
{
string s = " +"; //在开头加一个加号。这样可以防止一开始就有取反符
s += ss; //加号左边是空。我们会默认返回0
check(s); //看看有没有多余的空格
chu0 = false; //是否除0要重置
int ll = s.size()-1;
int i = 1;
while (i <= ll-1)
{ //这里是取反符的改变方法
if (i > 1 && s[i] == '-' && s[i-1]!=']' && (s[i+1] > '9' || s[i+1] <'0'))
{ //判断是否为取反符的方法
s = s.insert(i,"([0,0]");//把它变成减法
i+=6; //i指向'-'
i++;
int j = i;
if (s[i] == '(') //如果后面是括号则要跳过括号内的内容
{
int b = 0;
do
{
if (s[i] == '(') b++;
if (s[i] == ')') b--;
i++;
}
while (b!=0);
s = s.insert(i,")");//加一个右括号
i = j;//返回之前的位置。因为括号里可能也有取反符
}
if (s[i] == '[') //如果是个区间。只要到区间右边加括号就好
{
while (s[i] != ']') i++;
i++;
s = s.insert(i,")");
} ll = s.size();//要重新获取字符长度
}
i++; //递增指针
}
qujian l = reduce(s); //获取答案区间
if (chu0) //是否除0做出判断
printf("Division by zero\n");
else
printf("[%.3lf,%.3lf]\n",l.l,l.r); }
} int main()
{
input_data();
return 0;
}
【t094】区间运算的更多相关文章
- SICP 练习 (2.9)解决摘要:宽度和区间运算的关系间隔
SICP 2.9 像是一个数学题,要我们证明区间的和与差的宽度是被加和被减的区间的宽度的函数,而对于乘法和除法来说不成立. 书中所谓宽度就是区间起点和终点差的一半.以我看来更像是区间宽度的一半.无论怎 ...
- (线段树 区间运算求点)Flowers -- hdu -- 4325
http://acm.hdu.edu.cn/showproblem.php?pid=4325 Flowers Time Limit: 4000/2000 MS (Java/Others) Mem ...
- 当AS3遇见Swift(一)
当AS3遇见Swift 从Hello开始 As3 trace(“Hello Eko”) Swift println(“Hello Eko”) 挺象,有点隔壁王叔叔的意思. 常量和变量 As3 publ ...
- Spring表达式语言 之 5.3 SpEL语法(拾肆)
5.3 SpEL语法 5.3.1 基本表达式 一.字面量表达式: SpEL支持的字面量包括:字符串.数字类型(int.long.float.double).布尔类型.null类型. 类型 示例 字 ...
- Guava 8-区间
范例 List scores; Iterable belowMedian =Iterables.filter(scores,Range.lessThan(median)); ... Range val ...
- swift3.0基础语法
swift 3.0 基础语法 目录 01-变量和常量 02-运算符 03-可选项 04-条件语句 05-循环 06-字符串 07-元组 08-数组 09-字典 10-对象和类 11-枚举 12-属性 ...
- Asp.Net中的消息处理---MSMQ系列学习(一)
刚刚毕业一年,比较浮躁,上次面试被问到消息队列,觉得非常的惭愧因为不知道,所以下定决心一定要学会使用它.以前只是听说过有这么个东西,要说是什么,在什么场景下使用却是无从知晓,因为自己也确实没有在项目中 ...
- Boost程序库完全开发指南——深入C++“准”标准库(第3版)
内容简介 · · · · · · Boost 是一个功能强大.构造精巧.跨平台.开源并且完全免费的C++程序库,有着“C++‘准’标准库”的美誉. Boost 由C++标准委员会部分成员所设立的Bo ...
- C 语言资源大全中文版
C 语言资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-c 是 koz.ross 发起维护的 C 语言资源列表,内容包括了: ...
随机推荐
- python投票统计程序,统计序列中各个数值的份数,字典的应用。
这里可能会用到字典的知识, 字典主要是用来存储最后的统计结果. 字典的用法:http://www.runoob.com/python/python-dictionary.html https://ww ...
- JSP学习 —— 开篇:JSP,servlet容器,Tomcat,servlet容器之间的关系
JSP(JAVA SERVER PAGE)的缩写,其本身就是servlet的简化,是一种动态网页标准,其特点是在HTML代码中嵌入JAVA代码,JSP标签或用户标签来生成网页.至于它为什么会出现,主要 ...
- ECMall 25个 数据库表 说明文档
ecm_acategory //文章分类表 字段 类型 Null 默认 注释 cate_id int(10) 否 自增ID号,分类ID号 cate_name varchar(100) 否 分类 ...
- Loadrunner--web_find和web_reg_find的用法和区别
一.web_find()函数 该函数的作用是“在页面中查找相应的内容”,常用参数及含义如下: web_find("web_find", //定义该查找函数的名称 "Rig ...
- Docker---(1)Docker 简介
原文:Docker---(1)Docker 简介 版权声明:欢迎转载,请标明出处,如有问题,欢迎指正!谢谢!微信:w1186355422 https://blog.csdn.net/weixin_39 ...
- java程序猿经常使用的工具名称--知道中文意思吗
在学习java的时候常常会碰到一些单词,可是一般的时候也不是非常在意这个单词的意思,而是能够了解到这个工具或者框架能够做什么就能够了.偶尔总结了一下还蛮有意思的.例如以下, 假设有遗漏,各位能够帮忙补 ...
- 想在子线程里面触发的信号的槽函数在子线程执行,信号槽连接必须使用DirectConnection 方式(即使跨线程,也可以强迫DirectConnection,而不能是AutoConnection)
Qt多线程的实现 1.继承QThread,重新run 2.继承Object,调用moveToThread方法 两种方法各有利弊:主要参考:http://blog.51cto.com/9291927/1 ...
- sqlplus中怎么将你全部的操作和结果记录保存到你指定的文件里
[在sqlplus的操作中,非常多时候我们都想把自己的写的sql语句和改动日志或者结果信息做记录] [首先]肯定要正常连接到oralce数据库. [然后] 你用你指定的用户登录到oralce数据库之后 ...
- [Yarn] Use Yarn to Create an Alternative Import Name of an Installed Library
In this lesson we'll show how to use yarn to alias the names of same npm libraries but install diffe ...
- Injector Job深入分析 分类: H3_NUTCH 2015-03-10 15:44 334人阅读 评论(0) 收藏
Injector Job的主要功能是根据crawlId在hbase中创建一个表,将将文本中的seed注入表中. (一)命令执行 1.运行命令 [jediael@master local]$ bin/n ...