• 【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
    ");
    					else
    						printf("[%.3lf,%.3lf]
    ",l.l,l.r);	
    					
    		}
    }
    
    int main()
    {
    	input_data();	
    	return 0;	
    }


  • 相关阅读:
    Java设计模式之依赖倒置原则
    windows 下安装apache 遇到的问题
    Java序列化相关
    批量插入————优化
    接口相关
    Redis使用及工具类
    面试回顾——kafka
    面试回顾——List<T>排序
    Java面试——线程池
    面试回顾——session相关
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632346.html
Copyright © 2020-2023  润新知