【NOIP2011模拟9.15】区间运算

Description
区间运算是数学的一个领域。在区间运算中,常量和变量并不表示为一个单独、精确的
值,而是表示为一个有着上界和下界的区间或范围。在普通的运算中,一个数量可以表示为数轴上的一个点;而在区间运算中,一个数量表示数轴上的一段,例如[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]

Input

程序的输入包含一行或多行区间运算的中缀表达式,每个区间都表示为[min,max],表
达式中包括括号、负号(-)、加号(+)、减号(-)、乘号(*)和除号(/),括号可能是嵌套的。每一行
中都有可能有空格,但空格不会在表示区间的括号“[min,max]”中间或负号后出现。程序
不需要处理科学记数法(如2E6=2000000)。每一行均不超过80 个字符。运算采用标准的优先
级规则。下面按优先级递减的顺序给出各个运算符:
() 括号
- 取相反数
* / 乘法和除法,同级运算按从左往右的顺序
+ - 加法和减法,同级运算按从左往右的顺序

Output

对于输入的每一行,都相应地输出一行,输出的内容就是区间运算的最后结果,用
[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]

Data Constraint

The Solution

一道码农题。。。(⊙﹏⊙)b。。。

对于进行的运算。

设:

a=[x1,y1],b=[x2,y2]

a+b=[x1+x2,y1+y2]

ab=[x1y2,y1x2]

ab=[min(x1x2,x1y2,y1x2,y1y2),max(x1x2,x1y2,y1x2,y1y2)]

a/b[min(x1/x2,x1/y2,y1/x2,y1/y2),max(x1/x2,x1/y2,y1/x2,y1/y2)]

对于取反。一开始的时候先判断出哪些是取反符号(很容易就能想到要怎么判断的。)

然后在取反符号前加”([0,0]”,然后在取反符号后面恰当的位置加上”)”。
就是把取反运算变成一个减法运算。

然后利用二分的方法来求表达式。

Specific

先找到一个表达式里面运算的优先级别最小的运算符。
然后递归这个运算符左边的表达式。递归右边的表达式。
如果当前处理的表达式全为区间。则返回这个区间的端点。存在struct结构体中。
递归返回一个结构体。
同一优先级别的操作符。往后找。这样可以保证同一优先级。从左到右运算的顺序。

CODE

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
using namespace std;
bool Flag=false;
struct note
{
    double l,r;
};
void Extra_Space(string &s)
{
    int len=s.size()-1;
    fo(i,1,len)
        if (s[i]==' ') s[i]='$';
    string st=" ";
    fo(i,1,len) if (s[i]!='$') st+=s[i];
    s=st;
}
bool Check_Remove_Bracket(string &s)
{
    int len=s.size()-1;
    if (s[1]!='('||s[len]!=')') return false;
    int tot=0;
    fo(i,2,len-1)
    {
        if (s[i]=='(') tot++;
        if (s[i]==')') tot--;
        if (tot<0) return false;
    }
    s=s.erase(len,1);
    s=s.erase(1,1);
    return true;
}
bool Senior_pd(char x,char y) //1 > 0 <
{
    if (x=='&') return 1;
    if (x=='*' || x=='/') return 1;
    if ((x=='+'||x=='-') && (y=='+' || y=='-')) return 1;
    return 0;
}
int Find_Smallest_fu(string s)
{
    char now='&';
    int len=s.size()-1;
    int i=1,k;
    while (i<=len)
    {
        if (s[i]=='(')
        {
            int tot=0;
            do
            {
                if (s[i]=='(') tot++;
                if (s[i]==')') tot--;
                i++;
            }
            while (tot!=0);
        }
        else
        {
            if ((s[i]=='*'||s[i]=='/'||s[i]=='+') || (s[i]=='-'&&i>1&&(s[i-1]==']'||s[i-1]==')')))
            {
                if (Senior_pd(now,s[i])==1)
                {
                    now=s[i];
                    k=i;
                }
            }
            i++;
        }   
    }
    return k;
}
double Maxest(double x,double y,double xx,double yy)
{
    double mx=x;
    if (y>mx) mx=y;
    if (xx>mx) mx=xx;
    if (yy>mx) mx=yy;
    return mx;
}
double Minest(double x,double y,double xx,double yy)
{
    double mn=x;
    if (y<mn) mn=y;
    if (xx<mn) mn=xx;
    if (yy<mn) mn=yy;
    return mn;
}
note Ask(string s)
{
    note t;
    if (Flag) return t;
    if (s==" ")
    {
        t.l=0;
        t.r=0;
        return t;
    }
    bool bz=false;
    int len=s.size()-1;
    fo(i,1,len)
        if (s[i]=='*'||s[i]=='/'||s[i]=='+')
        {
            bz=true;
            break;
        }
        else if (s[i]=='-'&&i>1&&(s[i-1]==']'||s[i-1]==')'))
        {
            bz=true;
            break;
        }
    if (!bz)
    {
        int Pos1=s.find('['),Pos2=s.find(',');
        string st=s.substr(Pos1+1,Pos2-Pos1-1);
        int Pos3=s.find(']');
        string s2=s.substr(Pos2+1,Pos3-Pos2-1);
        t.l=atof(st.c_str());
        t.r=atof(s2.c_str());
        if (t.l>t.r) swap(t.l,t.r);
        return t;
    }
    bool bj=Check_Remove_Bracket(s);
    while (bj) bj=Check_Remove_Bracket(s);
    int Pos_fu=Find_Smallest_fu(s);
    string sl=s.substr(0,Pos_fu);
    char fu=s[Pos_fu];
    s=s.erase(1,Pos_fu);
    string sr=s;
    note t1=Ask(sl),t2=Ask(sr);
    if (Flag) return t1;
    note t3;

    if (fu=='+')
    {
        t.l=t1.l+t2.l;
        t.r=t1.r+t2.r;
    }
    else
    if (fu=='-')
    {
        t.l=t1.l-t2.r;
        t.r=t1.r-t2.l;
    }
    else
    if (fu=='*')
    {
        t.l=Minest(t1.l*t2.l,t1.l*t2.r,t1.r*t2.l,t1.r*t2.r);
        t.r=Maxest(t1.l*t2.l,t1.l*t2.r,t1.r*t2.l,t1.r*t2.r);
    }
    else
    if (fu=='/')
    {
        if (t2.l<=0 && t2.r>=0)
        {
            Flag=true;
            return t2;
        }
        t.r=Maxest(t1.l/t2.l,t1.l/t2.r,t1.r/t2.l,t1.r/t2.r);
        t.l=Minest(t1.l/t2.l,t1.l/t2.r,t1.r/t2.l,t1.r/t2.r);
    }
    return t;
}
int main()
{
    string t;
    while (getline(cin,t))
    {
        string s=" +";
        s+=t;
        Extra_Space(s);
        Flag=false; 
        int len=s.size()-1;
        int i=1;
        while (i<=len-1)
        {
            if (i>1&&s[i]=='-'&&s[i-1]!=']'&&(s[i+1]>'9'||s[i+1]<'0'))
            {
                s=s.insert(i,"([0,0]");
                i+=7;
                int j=i;
                if (s[i]=='(')
                {
                    int tot=0;
                    do
                    {
                        if (s[i]=='(') tot++;
                        if (s[i]==')') tot--;
                        i++;
                    }while (tot!=0);
                    s=s.insert(i,")");
                    i=j;
                }
                if (s[i]=='[')
                {
                    while (s[i]!=']') i++;
                    s=s.insert(++i,")");
                }
                len=s.size();
            }
            i++;
        }
        note l=Ask(s);
        if (Flag) printf("Division by zero\n");
        else printf("[%.3lf,%.3lf]\n",l.l,l.r);
    }
    return 0;
}

这是其他人的,附上代码解释。

#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);
     // 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;     
}  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值