【NOIP2011模拟9.15】区间运算——栈解决

【NOIP2011模拟9.15】区间运算

Time Limits: 1000 ms Memory Limits: 131072 KB

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 个字符。运算采用标准的优先级规则。下面按优先级递减的顺序给出各个运算符:
1. () 括号
2. - 取相反数
3. * / 乘法和除法,同级运算按从左往右的顺序
4. + - 加法和减法,同级运算按从左往右的顺序

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]

有些朋友都认为,关于字符串中的运算都不怎么好处理,其实是这样的。
对于这道题,让我们当一个简易的计算器,对这样的字符串进行处理也是比较难的。
但是我们还是对症下药,找到了解决这种问题的最优方法——栈,栈的复杂度是十分优良的,空间和时间都基本可以在O(n)上解决。接下来我就介绍这种用栈来解算式问题的方法。

我们先对单纯的算式来讲解。

首先,我们可以在算式的两侧加上括号。

如3+5变成(s+5),(2 * 5+3)/2-1变成((2 * 5+3)/2-1)。
我们就可以发现算式中字符出现的规律:

左括号->负号->数字->运算符号或者右括号

于是我们可以针对这样的顺序进行栈处理。

首先:如果遇到了左括号,就进行压栈处理

while(st[h]=='(')push(),h++;
void push()
{
    sym[++top]=st[h];
}

第二:如果遇到负号,就把它改成‘=’避免与减号弄混,压入栈中

if(st[h]=='-')
{
    st[h]='=';
    push();h++;
}

第三:对数字的处理,找出数字的那一串数,转化为数字,放到当前符号对应的位置中

if(st[h]>='0' && st[h]<='9')
{
    t=h;
    while(st[h]>='0' && st[h]<='9')++h;
    PutNum(t,h-1);
}
void PutNum(int h,int t)
{
    int number=0;
    for(int i=h;i<=t;i++)number+=(number<<3)+number+st[i]-'0';
    num[top]=number;
}

最后:对符号和右括号的处理:
符号,判断优先级:

bool can()
{
    if((st[h]=='+' || st[h]=='-' || st[h]=='*' || st[h]=='/')&&(sym[top]=='('))return 0;
    if(sym[top]=='=')return 1;
    if((st[h]=='*' || st[h]=='/')&&(sym[top]=='+' || sym[top]=='-'))return 0;
    return 1;
}

然后计算:

void pop()
{
    top--;
    if(sym[top+1]=='=')num[top]=-num[top+1];
    if(sym[top+1]=='+')num[top]+=num[top+1];
    if(sym[top+1]=='-')num[top]-=num[top+1];
    if(sym[top+1]=='*')num[top]*=num[top+1];
    if(sym[top+1]=='/')
    {
        if(!num[top+1])
        {
            printf("Divided By Zero!");
            p=1;return;
        }
        num[top]/=num[top+1];
    }
}

压栈:

while(can())
{
    pop();if(p)return 0;
}
push();

右括号处理,运算,弹栈:

while(st[h]=='(')push(),h++;
if(st[h]=='-')
{
    st[h]='=';
    push();h++;
    }
if(st[h]>='0' && st[h]<='9')
{
    t=h;
    while(st[h]>='0' && st[h]<='9')++h;
    PutNum(t,h-1);
}

所以,对算式的运算代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

int len,num[100003],top,h,t;
char st[100003],sym[100003];
bool p=0;

int GetLength();
void push();
void PutNum(int,int);
void pop();
bool can();

int main()
{
    memset(st,0,sizeof(st));
    scanf("%s",st+1);
    len=GetLength()+2;
    for(int i=len-1;i>=2;i--)st[i]=st[i-1];
    st[1]='(';
    st[len]=')';
    top=0;
    h=1;
    while(h<len)
    {
        while(st[h]=='(')push(),h++;
        if(st[h]=='-')
        {
            st[h]='=';
            push();h++;
        }
        if(st[h]>='0' && st[h]<='9')
        {
            t=h;
            while(st[h]>='0' && st[h]<='9')++h;
            PutNum(t,h-1);
        }
        do
        {
            if(st[h]==')')
            {
                while(sym[top]!='(')
                {
                    pop();if(p)return 0;
                }
                top--;
                num[top]=num[top+1];
            }else
            {
                while(can())
                {
                    pop();if(p)return 0;
                }
                push();
            }
            h++;
        }while(h<=len && st[h-1]==')');
    }
    printf("%d",num[0]);
} 

bool can()
{
    if((st[h]=='+' || st[h]=='-' || st[h]=='*' || st[h]=='/')&&(sym[top]=='('))return 0;
    if(sym[top]=='=')return 1;
    if((st[h]=='*' || st[h]=='/')&&(sym[top]=='+' || sym[top]=='-'))return 0;
    return 1;
}

void pop()
{
    top--;
    if(sym[top+1]=='=')num[top]=-num[top+1];
    if(sym[top+1]=='+')num[top]+=num[top+1];
    if(sym[top+1]=='-')num[top]-=num[top+1];
    if(sym[top+1]=='*')num[top]*=num[top+1];
    if(sym[top+1]=='/')
    {
        if(!num[top+1])
        {
            printf("Division By Zero!");
            p=1;return;
        }
        num[top]/=num[top+1];
    }
}

void PutNum(int h,int t)
{
    int number=0;
    for(int i=h;i<=t;i++)number+=(number<<3)+number+st[i]-'0';
    num[top]=number;
}

void push()
{
    sym[++top]=st[h];
}

int GetLength()
{
    int ans=0,w=0;
    while(st[++w])ans++;
    return ans;
}

区间运算也是一样,只是一个数换成了一个区间:
注意一下运算方式就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;

char st[85],sym[85];
double num[85][2];
int top,h,t,tot;
bool bz;

int Getlen();
void GetNum(int,int,int);
void push();
void pop();
bool can();

int main()
{
    freopen("algorithm.in","r",stdin);
    while(1)
    {
        memset(st,0,sizeof(st));
        memset(sym,0,sizeof(sym));
        top=0;
        scanf("%s\n",st+1);
        int len=Getlen()+2;
        for(int i=len-1;i>=2;i--)st[i]=st[i-1];
        st[1]='(';
        st[len]=')';
        if(len==2)break;
        bz=1;
        h=1;
        while(h<=len)
        {
            if(st[h]=='-')
            {
                st[h]='=';
                push();
                h++;
            }
            while(st[h]=='(')
            {
                push();
                h++;
            }
            if(st[h]=='-')
            {
                st[h]='=';
                push();
                h++;
            }
            if(st[h]=='[')
            {
                t=h;
                while(st[h]!=']')h++;
                for(int i=t+1;i<h;i++)if(st[i]==',')
                {
                    GetNum(t,i,h);
                    if(num[top][0]>num[top][1])
                    {
                        double x=num[top][0];
                        num[top][0]=num[top][1];
                        num[top][1]=x;
                    }
                    break;
                }
                h++;
            }
            do
            {
                if(st[h]==')')
                {
                    tot++;
                    while(sym[top]!='(')
                    {
                        pop();
                        if(!bz)break;
                    }
                    if(!bz)break;
                    num[top-1][1]=num[top][1];
                    num[top-1][0]=num[top][0];
                    --top;
                }else
                {
                    while(can())
                    {
                        pop();
                        if(!bz)break;
                    }
                    push();
                    if(!bz)break;
                }
                ++h;
            }while(h<=len && st[h-1]==')');
            if(!bz)break;
        }
        if(bz)printf("[%.3lf,%.3lf]\n",num[0][0],num[0][1]);
    }
}

void pop()
{
    top--;
    if(sym[top+1]=='=')
    {
        num[top][0]=-num[top+1][1];
        num[top][1]=-num[top+1][0];
    }
    if(sym[top+1]=='+')
    {
        num[top][0]=num[top][0]+num[top+1][0];
        num[top][1]=num[top][1]+num[top+1][1];
    }
    if(sym[top+1]=='-')
    {
        num[top][0]=num[top][0]-num[top+1][1];
        num[top][1]=num[top][1]-num[top+1][0];
    }
    if(sym[top+1]=='*')
    {
        double x,y,z,r;
        x=num[top][0]*num[top+1][0];
        y=num[top][0]*num[top+1][1];
        z=num[top][1]*num[top+1][0];
        r=num[top][1]*num[top+1][1];
        num[top][0]=min(x,min(y,min(z,r)));
        num[top][1]=max(x,max(y,max(z,r)));
    }
    if(sym[top+1]=='/')
    {
        if(num[top+1][0]<=0 && num[top+1][1]>=0)
        {
            printf("Division by zero\n");
            bz=0;return;
        }
        double x,y,z,r;
        x=num[top][0]/num[top+1][0];
        y=num[top][0]/num[top+1][1];
        z=num[top][1]/num[top+1][0];
        r=num[top][1]/num[top+1][1];
        num[top][0]=min(x,min(y,min(z,r)));
        num[top][1]=max(x,max(y,max(z,r)));
    }
}

bool can()
{
    if(sym[top]=='=')return 1;
    if((st[h]=='+' || st[h]=='-' || st[h]=='*' || st[h]=='/')&&(sym[top]=='('))return 0;
    if((st[h]=='*' || st[h]=='/')&&(sym[top]=='+' || sym[top]=='-'))return 0;
    return 1;
}

void push(){sym[++top]=st[h];}

int Getlen()
{
    int ans=0,w=1;
    while(st[w++])++ans;
    return ans;
}

void GetNum(int h,int s,int t)
{
    bool p=0;
    double mo=1.0;
    num[top][0]=num[top][1]=0.0;
    while(++h<s)
    {
        if(st[h]>='0' && st[h]<='9')
        {
            if(p)
            {
                num[top][0]=num[top][0]+(double)(st[h]-'0')*mo;
                mo=mo*0.1;
            }else
            {
                num[top][0]=num[top][0]*10.0+(st[h]-'0')*mo;
            }
        }
        if(st[h]=='.')
        {
            mo=mo*0.1;
            p=1;
        }
        if(st[h]=='-')mo=-mo;
    }
    p=0;
    mo=1.0;
    while(++s<t)
    {
        if(st[s]>='0' && st[s]<='9')
        {
            if(p)
            {
                num[top][1]=num[top][1]+(double)(st[s]-'0')*mo;
                mo=mo*10.0;
            }else
            {
                num[top][1]=num[top][1]*10.0+(double)((st[s]-'0')*mo);
            }

        }
        if(st[s]=='.')
        {
            mo/=10.0;
            p=1;
        }
        if(st[s]=='-')mo=-mo;
    }   
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值