1. 程式人生 > >NYOJ-35-表示式求值

NYOJ-35-表示式求值

描述
ACM隊的mdd想做一個計算器,但是,他要做的不僅僅是一計算一個A+B的計算器,他想實現隨便輸入一個表示式都能求出它的值的計算器,現在請你幫助他來實現這個計算器吧。
比如輸入:“1+2/4=”,程式就輸出1.50(結果保留兩位小數)

輸入
第一行輸入一個整數n,共有n組測試資料(n<10)。
每組測試資料只有一行,是一個長度不超過1000的字串,表示這個運算式,每個運算式都是以“=”結束。這個表示式裡只包含+-*/與小括號這幾種符號。其中小括號可以巢狀使用。資料保證輸入的運算元中不會出現負數。
資料保證除數不會為0

輸出
每組都輸出該組運算式的運算結果,輸出結果保留兩位小數。

樣例輸入
2
1.000+2/4=
((1+2)*5+1)/4=

樣例輸出
1.50
4.00

題目比較容易理解,就是實現四則運算。
說到四則運算,不得不提到逆波蘭表示法,也就是字尾表示法。這種表示法不需要括號,對於9 + (3 - 1) x 3 + 10 ÷ 2用字尾表示法的樣子則是:9 3 1 - 3 * + 10 2 / +。之所以叫字尾的原因是,所有的運算子號都是要在運算數字的後邊出現,那麼很容易的想到,我們平時寫的式子,也就是第一個式子,是中綴表示法。

中綴表示式轉字尾表示式規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出,即成為字尾表示式的一部分;若是符號,則判斷其與棧頂符號的優先順序,是右括號或者優先順序低於棧頂符號(乘除優先於加減)則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出字尾表示式為止,這裡我們需要一個棧來實現,用於符號的進棧出棧。

9 + (3 - 1) x 3 + 10 ÷ 2(中綴表示式) >>> 9 3 1 - 3 * + 10 2 / +(字尾表示式)

字尾表示式使用規則:從左到右遍歷字尾表示式的每一個數字和符號,遇到是數字就進棧,遇到是符號,就當處於棧頂的兩個數字出棧進行運算,運算結果進棧,一直到最終獲得結果。

這裡存在兩個過程,
1.將中綴表示式轉化為字尾表示式(棧用來進出運算子號)。
2.將字尾表示式進行運算得到結果(棧用來進出運算的數字)。

程式碼如下:

/*
不知道為啥一直WA,可是我試了很多資料都可以的,哎,頭疼死了。暫且記下,來日再戰,我需要靜靜。
後邊的兩個程式碼均是AC程式碼。
*/
#include <stdio.h> #include <string.h> #include <stdlib.h> #define INF 10000000 #define ADD INF + 1 //+ #define SUB INF + 2 //- #define MUL INF + 3 //x #define DIV INF + 4 //÷ char str[1005]; //原公式 int len; //公式長度 char symbol[1000]; //運算子號棧 float suffix[1005]; //字尾式 float answer[1000]; //運算棧 //從s[*pc]開始獲取一個浮點數 int StrToInt(char s[], int * pc, float *pout) { char buf[100]; int i = 0; if(s[*pc]<'0' || s[*pc]>'9') return 1; else { while((s[*pc] >= '0' && s[*pc] <= '9') || s[*pc] == '.') { buf[i] = s[*pc]; (*pc)++; i++; } buf[i] = '\0'; *pout = (float)atof(buf); return 0; } } void swi(int *key, char sym) { switch (sym) { case '+': suffix[(*key)++] = ADD; break; case '-': suffix[(*key)++] = SUB; break; case '*': suffix[(*key)++] = MUL; break; case '/': suffix[(*key)++] = DIV; break; } } void perform(int suf, int *_key) { float a = answer[(*_key)--]; float b = answer[(*_key)]; float c; switch (suf) { case (INF + 1): c = a + b; break; case (INF + 2): c = b - a; break; case (INF + 3): c = a * b; break; default: c = b / a; break; } answer[*_key] = c; // printf("%.2f\n", answer[*_key]); return ; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%s", str); len = (int)strlen(str) - 1; int key = 0; int top = -1; float pout; //中綴式轉字尾式 for (int i = 0; i < len; ) { if (!StrToInt(str, &i, &pout)) { suffix[key++] = pout; } else if (top != -1 && str[i] == ')') { while (symbol[top] != '(') { swi(&key, symbol[top--]); } i++; top--; } else if (top != -1 && (str[i] == '+' || str[i] == '-') && (symbol[top] == '*' || symbol[top] == '/')) { while (symbol[top] != '(' && top >= 0) { swi(&key, symbol[top--]); } symbol[++top] = str[i++]; } else if (top != -1 && (str[i] == '*' || str[i] == '/') && (symbol[top] == '*' || symbol[top] == '/')) { swi(&key, symbol[top]); symbol[top] = str[i++]; } else { symbol[++top] = str[i++]; } } while (top >= 0) { swi(&key, symbol[top--]); } // for (int i = 0; i < key; i++) // { // printf("%f ", suffix[i]); // } // printf("\n"); //字尾式運算 int _key = -1; for (int i = 0; i < key; i++) { if (suffix[i] < INF) { answer[++_key] = suffix[i]; // printf("%.2f\n", answer[_key]); } else { perform((int)suffix[i], &_key); } } printf("%.2f\n", answer[0]); } return 0; }

另外還有兩種方法,第一種是將字元和資料分別入兩個棧,然後根據優先順序的比較,對資料棧頂的兩個元素進行出棧操作然後進棧。

#include<stdio.h>
#include<stdlib.h>

//資料棧
typedef struct DA
{
    float data[1000];
    int pop;
} SDA;

//運算子棧
typedef struct OP
{
    char op[1000];
    int pop;
} SOP;

//初始化資料棧
int InitSDA(SDA * p)
{
    p->pop = 0;
    return 0;
}

//初始化運算子棧
int InitSOP(SOP * p)
{
    p->pop = 0;
    (p->op[p->pop]) = '=';
    (p->pop)++;
    return 0;
}

//資料入棧
int PushSDA(SDA * p, float d)
{
    if(p->pop < 1000)
    {
        p->data[p->pop] = d;
        (p->pop)++;
        return 0;
    }
    else
        return 1;   //棧滿
}

//運算子入棧
int PushSOP(SOP * p, char c)
{
    if(p->pop < 1000)
    {
        p->op[p->pop] = c;
        (p->pop)++;
        return 0;
    }
    else
        return 1;   //棧滿
}

//資料出棧
int PopSDA(SDA * p, float * d)
{
    (p->pop)--;
    if(p->pop >= 0)
    {
        *d = p->data[p->pop];
        return 0;
    }
    else
        return 1;
}

//運算子出棧
int PopSOP(SOP * p, char * c)
{
    (p->pop)--;
    if(p->pop >= 0)
    {
        *c = p->op[p->pop];
        return 0;
    }
    else
        return 1;
}

//從s[*pc]開始獲取一個浮點數
int StrToInt(char s[], int * pc, float *pout)
{
    char buf[100];
    int i = 0;

    if(s[*pc]<'0' || s[*pc]>'9')
        return 1;
    else
    {
        while((s[*pc] >= '0' && s[*pc] <= '9') || s[*pc] == '.')
        {
            buf[i] = s[*pc];
            (*pc)++;
            i++;
        }
        buf[i] = '\0';
        *pout = (float)atof(buf);
        return 0;
    }
}

//從s[*pc]獲取一個char
int StrToChar(char s[], int *pc, char *pout)
{
    if('+'==s[*pc] || '-'==s[*pc] || '*'==s[*pc] || '/'==s[*pc] || '('==s[*pc] || ')'==s[*pc])
    {
        *pout = s[*pc];
        (*pc)++;
        return 0;
    }
    else
        return 1;
}

//獲取優先順序
char GetPri(char c1, char c2)
{

    char f[7][7] = {'>', '>', '<', '<', '<', '>', '>',
        '>', '>', '<', '<', '<', '>', '>',
        '>', '>', '>', '>', '<', '>', '>',
        '>', '>', '>', '>', '<', '>', '>',
        '<', '<', '<', '<', '<', '=', '\0',
        '>', '>', '>', '>', '\0', '>', '>',
        '<', '<', '<', '<', '<', '\0', '=',};

    int i=0, j=0;
    switch(c1)
    {
        case '+': i = 0; break;
        case '-': i = 1; break;
        case '*': i = 2; break;
        case '/': i = 3; break;
        case '(': i = 4; break;
        case ')': i = 5; break;
        case '=': i = 6; break;
    }
    switch(c2)
    {
        case '+': j = 0; break;
        case '-': j = 1; break;
        case '*': j = 2; break;
        case '/': j = 3; break;
        case '(': j = 4; break;
        case ')': j = 5; break;
        case '=': j = 6; break;
    }
    return f[i][j];
}

//計算表示式
float Operate(float a, char op, float b)
{
    switch(op)
    {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
        default: return 0;
    }
}

int main(void)
{
    char s[10][1000];
    int c = 0;
    float bufda;
    char bufop;
    float a, b;
    SDA sda;
    SOP sop;
    int n;
    int i;


    scanf("%d", &n);
    for(i = 0; i < n; i++)
        scanf("%s", s[i]);
    for(i = 0; i < n; i++)
    {
        c = 0;
        InitSDA(&sda);  //初始化資料棧
        InitSOP(&sop);  //初始化符號棧
        while(s[i][c] != '=' || sop.op[sop.pop - 1] != '=') //  計算未完成
        {
            if(0 == StrToInt(s[i], &c, &bufda))
                PushSDA(&sda, bufda);   //資料入棧
            else
            {
                switch(GetPri(sop.op[sop.pop - 1], s[i][c]))
                {
                    case '<':
                        if(0 == StrToChar(s[i], &c, &bufop))
                            PushSOP(&sop, bufop);
                        break;
                    case '=':
                        PopSOP(&sop, &bufop);
                        c++;
                        break;
                    case '>':
                        PopSOP(&sop, &bufop);
                        PopSDA(&sda, &b);
                        PopSDA(&sda, &a);
                        PushSDA(&sda, Operate(a, bufop, b));
                        break;
                }
            }
        }
        PopSDA(&sda, &a);
        printf("%.2f\n", a);
    }
    return 0;
}


//改寫成C++,並簡化後如此
//#include <stack>
//#include <stdio.h>
//#include <ctype.h>
//#include <string.h>
//#include <stdlib.h>
//
//using namespace std;
//
//int priority(char c)
//{
//    if(c == '=')    return 0;
//    if(c == '+')    return 1;
//    if(c == '-')    return 1;
//    if(c == '*')    return 2;
//    if(c == '/')    return 2;
//    return 0;
//}
//
//void compute(stack<double>& Num,stack<char>& Op)
//{
//    double b = Num.top();
//    Num.pop();
//    double a = Num.top();
//    Num.pop();
//    switch(Op.top())
//    {
//        case '+':Num.push(a+b);break;
//        case '-':Num.push(a-b);break;
//        case '*':Num.push(a*b);break;
//        case '/':Num.push(a/b);break;
//    }
//    Op.pop();
//}
//
//int main()
//{
//    int z;
//    char str[1005];
//    stack<double> Num;
//    stack<char> Op;
//    scanf("%d",&z);
//    while(z--)
//    {
//        scanf("%s",str);
//        int len = strlen(str);
//        for(int i=0;i<len;i++)
//        {
//            if(isdigit(str[i]))
//            {
//                double n = atof(&str[i]);
//                while(i<len && (isdigit(str[i]) || str[i]=='.'))
//                    i++;
//                i--;
//                Num.push(n);
//            }
//            else
//            {
//                if(str[i] == '(')
//                    Op.push(str[i]);
//                else if(str[i] == ')')
//                {
//                    while(Op.top()!='(')
//                        compute(Num,Op);
//                    Op.pop();
//                }
//                else if(Op.empty() || priority(str[i])>priority(Op.top()))
//                    Op.push(str[i]);
//                else
//                {
//                    while(!Op.empty() && priority(str[i])<=priority(Op.top()))
//                        compute(Num,Op);
//                    Op.push(str[i]);
//                }
//            }
//        }
//        Op.pop();
//        printf("%.2f\n",Num.top());
//        Num.pop();
//    }
//    return 0;
//}

第二種是動態規劃,將一個大問題切割成些許小問題,和歸併排序的思想相仿。

// AC(動態規劃)
#include<stdio.h>
#include<string.h>

int len;
int fst[1005];
char str[1005];
double Jud(int begin, int end); /*計算並返回表示式在區間[begin end]中的值*/

int main()
{
    int T, i;
    double ans;
    scanf("%d", &T);
    while(T--)
    {
        memset(fst, 0, sizeof(fst)); /*一定要清0*/
        scanf("%s", str);
        len = (int)strlen(str)-1;
        fst[0] = 1;
        for(i = 1; i <= len - 1; i++) /*fst[i]表示優先順序,fst[i]越大,說明優先順序越高↓↓*/
        {                           /*例如str[] -- ((1+2)*5+1)/4=*/
            if(str[i - 1]== '(')    /*對應fst[] -- 12333222221110*/
                fst[i] = fst[i - 1] + 1;
            else if(str[i] == ')')
                fst[i] = fst[i - 1] - 1;
            else
                fst[i] = fst[i - 1];
        }
        ans = Jud(0, len - 1); /*傳入整個表示式,不包括=*/
        printf("%.2f\n", ans);
    }
    return 0;
}

double Jud(int begin, int end)
{ /*規定區間[begin, end]的優先順序標準為fst[begin]*/
    int i;
    double k;
    for(i = begin; i <= end; i++) /*先從做左到右找到第一個處於指定優先順序的'+'運算子*/
    {
        if(str[i]== '+' && fst[i] == fst[begin])
        {
            k = Jud(begin, i - 1) + Jud(i + 1, end); /*將其拆成兩個個表示式的和*/
            return k;
        }
    }
    for(i = end; i >= begin; i--) /*如果找不到'+',再從右往左找到第一個處於指定優先順序的'-'運算子*/
    {
        if(str[i]=='-' && fst[i] == fst[begin])
        {
            k = Jud(begin, i - 1) - Jud(i + 1, end);    /*將其拆成兩個個表示式的差*/
            return k;
        }
    }
    for(i = begin; i <= end; i++)   /*如果還找不到,再從左往右找到第一個處於指定優先順序的'*'運算子*/
    {
        if(str[i] == '*' && fst[i] == fst[begin])
        {
            k = Jud(begin, i - 1) * Jud(i + 1, end);    /*將其拆成兩個個表示式的積*/
            return k;
        }
    }
    for(i = end; i >= begin; i--)   /*同上,從右往左找到第一個處於指定優先順序的'/'運算子*/
    {
        if(str[i] == '/' && fst[i] == fst[begin])
        {
            k = Jud(begin, i - 1) / Jud(i + 1, end); /*將其拆成兩個個表示式的商*/
            return k;
        }
    }
    if(str[begin]=='(') /*如果在這個[begin,end]區間裡的指定優先順序中沒有任何運算子,說明此區間可能完全包含上一級*/
    {
        for(i = begin + 1; fst[i] >= fst[begin + 1]; i++);
        k = Jud(begin + 1, i - 1);
    }
    else /*既然沒有包含上一級,說明這個區間就只剩下一個數啦*/
    {
        char *p = str;
        sscanf(p+begin, "%lf", &k); /*將這個數賦值給k,並返回*/
    }
    return k;
}

理論上這三種均可以AC…可是我不知道第一種哪裡出了BUG,等回頭有空了再看吧…就這樣吧。