【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;
}
}