W3 school 菜鸟教程 我要自学网 信息学奥赛NOI 花哥的博客 不逼自己一把,怎知自己有多优秀——校长语录

求助:一本通网站1356(己基本解决)

一本通网站1356

我写了以下代码:

//1356:计算(calc)
/*基本思路:后面优先级高,前面内容入栈,否则先算前面内容*/ 
#include<iostream>
#include<cstdio> 
#include<cstring> 
using namespace std;
int const N=2e6+2;
int stack1[N*2],cnt,x,y,x0,tp1,tp2;
char stack2[N],ch,ch0; 
string s;

//计算运算优先组,后面优先级高就前面内容入栈,后面优先级小于等于前面优先组就先算栈内内容 
int level(char p)
{
    if(p=='+'||p=='-')return 1;
    if(p=='*'||p=='/')return 2;
    if(p=='^')return 3;    
    return 0;
}
int cf(int x,int y)//一个简单快速幂求x^y 
{
    if(y==0)return 1;
    if(y==1)return x;
    int t=cf(x,y/2);
    t*=t;
    if(y%2)t*=x;
    return t;
}
//完成一次基本计算 
void calc()
{
    int x,y;//定义两个操作数 
    char ys;//定义一个运算符 
    ys=stack2[tp2--];
    y=stack1[tp1--];
    x=stack1[tp1];//运算一次取出两个数,并把计算结果存回,故此处没减栈顶指针 
    switch(ys)
    {
        case '+':stack1[tp1]=x+y;break;
        case '-':stack1[tp1]=x-y;break;
        case '*':stack1[tp1]=x*y;break;
        case '/':stack1[tp1]=x/y;break;
        default:stack1[tp1]=cf(x,y);//也可以直接用库函数pow(x,y) 
    }
}
int main(){    
    s[++cnt]='(';
    while((ch=getchar())&&ch!=13&&ch!=10)s[++cnt]=ch;
    s[++cnt]=')';
    for(int i=1;i<=cnt;i++)
    {
        ch=s[i];//ch可能为(、)、数字(包括负号)、和运算符 
        if(ch=='(')stack2[++tp2]='(';
        //遇到)就计算到(为止。遇到(之前,栈内所存运算符应该逐级上升,故需反向运算
        //如1+2*3^4
        else if(ch==')')
        {
            while(stack2[tp2]!='(')calc();
            tp2--;//让(出栈 
        }
        else if(ch>='0'&&ch<='9'||ch=='-'&&s[i-1]=='(') 
        //读到的是数字,把连续的数字变成数值 ,如果是'-'先判断前面是不是(,是则说明是负号不是减号 
        {
            if(ch=='-')x0=0,y=-1;//y代表数值的符号 
            else x0=ch-'0',y=1;
            ch0=s[++i];
            while(ch0>='0'&&ch0<='9')x0=x0*10+ch0-'0',ch0=s[++i];
            i--;
            x0*=y;
            stack1[++tp1]=x0;
        }
        else  //ch为运算符 
        {
            while(level(ch)<=level(stack2[tp2]))calc();
            //当前运算符不超过栈顶运算,先算栈顶运算 
            //此处未判断运算符栈是否为空是因为栈底是一个(,运算级最低,不可能超过当前运算等级    
            stack2[++tp2]=ch;//直到当前运算符高于栈顶运算符再把运算符存栈 
        }
    }
    cout<<stack1[tp1]<<endl;
    return 0;
}

有两个问题:1、在windows版DEV下运行结果如下

而且,在出现答案258后至少会等待3秒以上程序才会结束。(多次测试都是)

2、提交到网站后答案错误4个,运行错误一个。

 

然而,就这同一个思路改成直接用stl中的stack,得到如下代码:

//1356:计算(calc)
/*基本思路:后面优先级高,前面内容入栈,否则先算前面内容*/ 
#include<iostream>
#include<stack>
#include<cmath>
#include<cstring> 
using namespace std;
int const N=1e5+1;
int x,y;
char ch,ch0; 
string s;
stack<int>s1;
stack<char>s2;
//计算运算优先组,后面优先级高就前面内容入栈,后面优先级小于等于前面优先组就先算栈内内容 
int level(char p)
{
    if(p=='+'||p=='-')return 1;
    if(p=='*'||p=='/')return 2;
    if(p=='^')return 3;    
    return 0;
}

//完成一次基本计算 
void calc()
{
    int m,n;
    char z;
    n=s1.top();
    s1.pop();
    m=s1.top();
    s1.pop();
    z=s2.top();
    s2.pop();
    switch(z)
    {
        case '+':s1.push(m+n);break;
        case '-':s1.push(m-n);break;
        case '*':s1.push(m*n);break;
        case '/':s1.push(m/n);break;
        default:s1.push(pow(m,n));
    }
    return;
}
int main(){    
    cin>>s;
    s='('+s+')';
    int i=0;
    ch='(';
    do
    {
        if(ch=='(')
        {
            s2.push('(');
        }
        
        //遇到)就计算到(为止。遇到(之前,栈内所存运算符应该逐级上升,故需反向运算
        //如1+2*3^4 
        else if(ch==')')
        {
            while(s2.top()!='(')calc();
            s2.pop();//弹出( 
        }
        else if(ch>='0'&&ch<='9'||ch=='-'&&s[i-1]=='(') 
        //读到的是数字,把连续的数字变成数值 ,如果是'-'先判断前面是不是(,是则说明是负号不是减号 
        {
            if(ch=='-')x=0,y=-1;//是负号则符号设为-1,初始值为0 
            else x=ch-'0',y=1;//默认符号为正
            ch0=s[++i];
            while(ch0>='0'&&ch0<='9')x=x*10+ch0-'0',ch0=s[++i];
            i--;
            x*=y;
            s1.push(x);
        }
        else  //ch为运算符 
        {
            while(level(ch)<=level(s2.top()))//当前运算符不超过栈顶运算,先算栈顶运算 
            {
                calc();
            }
            s2.push(ch);//直到当前运算符高于栈顶运算符再把运算符存栈 
        }
    }while(ch=s[++i]);
    cout<<s1.top()<<endl;
    return 0;
}

便没有任何问题,提交后也全正确,万能的网络朋友们,请帮我看看第一个代码错在哪了?

 

   错因分析:对于一个string,虽然它跟char[]类似,但也不完全相同。它有固定结尾('\0'),是一个字符串结束的标记。虽然我们能像char[]一样一个一个地改变每一个字符的值,但,修改后的字符串并没有被系统认可。比如string s="a",这个字符串只有一个字符,存储在内存中其实是两个位置:s[0]='a',s[1]='\0'。字符串长度可用s.size()获得,当然这个值是1(‘\0'结束标记不纳入计数之列),如果我们人为修改s[1]='b',尽管你可以再加上一句:s[2]='\0',(感觉像是做错后的掩饰),但此时测试s.size()=1,这就说明我们修改字符方式加入的'b'并没有被字符串接受。此时如果想输出字符串,比如:cout<<s;结果也是a,没有b。再做一个测试:string s="a"; s[1]='b'; s[2]='c'; s[3]='d';此时,若cout<<s;可得到a。如果输出s[1]、s[2]、s[3]都可以得到相应的字符。若再加上一句: s+='e';然后cout<<s;显示结果会是如何?答案是ae。如果你继续显示s[1]、s[2]、s[3],那会是'\0'。

  或许可以换个角度理解:string s;定义了,再赋值,或定义时就赋值,一旦赋值完成,那么与s等效的字符数组就确定了,数组的最大下标也就确定了(s.size()就代表字符串的长度,也可以说是字符数组不越界能访问的最大下标),如前文所说s="a",那s[2]实质上已经发生下标越界了,内存数据将会混乱,在本机还能运行,可能由于开发系统的保护抑或这个混乱尚未造成可见的错误,但在真实测试数据(数据量远比样例大得多)到来,又离开了IDE的保护,错误就显现出来了。这或许可以这么说:你的错误操作未必会得出错误结果。

  如果想增加一个字符'b’并被系统认同,当然也很简单:s+='b';,这一个操作的背后,除了在后面加了一个字符外,还实时地修改了s.size(),这个操作是配套的,也就是说同步修改了数组的边界,那就不会发生下标越界问题了。还有一事大家需要明白:s.size()是不可人为手动修改的。比如s.size()=3;这是错误的。显然嘛,你看size后面有一个括号,说明它是一个函数返回值,没有输入功能啊。所以,手动修改s[3]='b'这样的操作没办法同步边界,越界和错误是必须的。

   要想纠正这个错误那就简单了,方法也很多,本文就不讨论了。(参看第二版本)(写第一版本输入本想试下不同的输入方法的,没想搞出这么大一乌龙,收不到场了。)

 

  谢谢之江学院石老师的不断指点!!!

posted @ 2020-03-30 17:31  耍人  阅读(1229)  评论(0编辑  收藏  举报