求助:一本通網站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'這樣的操作沒辦法同步邊界,越界和錯誤是必須的。

   要想糾正這個錯誤那就簡單了,方法也很多,本文就不討論了。(參看第二版本)(寫第一版本輸入本想試下不同的輸入方法的,沒想搞出這么大一烏龍,收不到場了。)

 

  謝謝之江學院石老師的不斷指點!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM