P1310 表達式的值


原題鏈接 https://www.luogu.org/problemnew/show/P1310

water_lift 一波講解,然后我們就會了這個題,然后我們就要寫博客啦QwQ~

這是個布爾表達式基計數問題。

這個其實⊕就是“|”或運算,×就是“&”與運算。

我們將這個式子通俗得看成:x&y 和 x|y

我們設x0是使x為0的方案數,x1是使x為1的方案數;y0是使y為0的方案數,y1是使y為1的方案數;

使x&y為1的方案數?為0的方案數?

使x|y為1的方案數?為0的方案數?

不難發現:

使x&y為1,那么xy都要為1,所以方案數為x1y1

使x&y為0,那么xy不都為1,所以方案數為x1y0+x0y1+x0y0

使x|y為1的方案數為x1y1+x0y1+x1y0,為0的方案數為x0y0

 

然后我們發現給出的式子一個特別煩的東東就是:   有括號

為了去掉括號,我們可以考慮將題目輸入的中綴表達式給它換成后綴表達式(后綴表達式的好處就是式子中沒有括號!)

腫么換呢?這確實是一個難題,還好我們找到了這樣一個方法(由於太蒟還不會證正確性):

遍歷中綴表達式:

  1. 遇到數字,直接放入答案序列

  2. 遇到左括號,入棧

  3. 遇到右括號,把棧頂到上一個左括號的元素依次出棧並放入答案序列
  4. 遇到乘號,入棧
  5. 遇到加號,從棧頂開始彈出這段連續的乘號,並放入答案序列,最后加號入棧
  6. 最后把棧里剩下的元素依次放入答案序列

為什么是正確的呢?貼一下water_lift的模擬過程(想要更豐富的展現?請看water_lift的博客):

 

那么有了后綴表達式,這么求值?

這個想必大家都會了,我們用棧就能輕松搞定qwq:

遇到數字入棧,遇到運算符號就取出棧頂的兩個元素,將它們進行該運算符的運算后再入棧。

 

The last question:

題目要求的是使得表達式為0的方案數,那么在哪填數字?

看一下樣例: 

 +(*)

很顯然就是在這幾個標紅的位置填數字啦:

_+(_*_

So,我們可以總結一下在哪里填數字: 式子最前面一定要填個數字(你見哪個式子是符號打頭?),然后“+”和“×”后面要填一個數字!

 

到這里咱們就可以食用代碼啦:

#include <bits/stdc++.h>
using namespace std;
stack<char> fh;                                 //中綴表達式轉后綴表達式的時候所要用到的存符號的棧 
stack<int> zero;                                //一個存使表達式為0的方案數的棧 
stack<int> one;                                 //一個存使表達式為1的方案數的棧 
string houxu;                                   //轉化后的后綴表達式 
char ch[100001];                                //存輸入的字符串 
int n,l0,l1,r0,r1;                              
//這里l0就是上文的x0,l1是x1,r0是y0,r1是y1 
const int mod=10007;
int main()
{
    scanf("%d",&n);
    scanf("%s",ch+1);                           //ch+1是讓它從下標為1開始讀的     
    houxu.push_back('n');                       //后綴表達式的最前頭要有個數字,用字符'n'來表示這里應該填數字 
    for(int i=1;i<=n;i++)
    {
        if(ch[i]=='('||ch[i]=='*')              //如果是'('或'*',正常入符號棧 
        {
            fh.push(ch[i]);
        }
        if(ch[i]=='+')                          //如果是'+',要把符號棧棧頂的'*' 都放入后綴表達式的后面 
        {
            while(!fh.empty()&&fh.top()=='*')
            {
                houxu.push_back(fh.top());
                fh.pop();
            }
            fh.push(ch[i]);
        }
        if(ch[i]==')')                          //如果是')',則要把'('和')'之間的符號放入后綴表達式的后面         
        {
            while(fh.top()!='(')
            {
                houxu.push_back(fh.top());
                fh.pop();
            }
            fh.pop();                           //彈出左括號 
        }
        if(ch[i]!='('&&ch[i]!=')') houxu.push_back('n');   //如果不是括號,也就是說如果是'+'或'*',那么就要在后面填一個數字 
    }
    while(!fh.empty())                          //將符號棧中剩余的符號全部放入后綴表達式  
    {
        houxu.push_back(fh.top());
        fh.pop();
    }
    for(int i=0;i<houxu.size();i++)             //可以看成是后綴表達式求值 
    {
        char c=houxu[i];
        if(c=='n')                              //如果這里該填數字   
        {
            zero.push(1);                       //該數字填0的方案有1種 
            one.push(1);                        //該數字填1的方案有1種 
        }
        else                                    //如果是符號 
        {
            l0=zero.top();zero.pop();           //彈出一個使l為0的方案數 
            l1=one.top();one.pop();             //彈出一個使l為1的方案數 
            r0=zero.top();zero.pop();           //彈出一個使r為0的方案數
            r1=one.top();one.pop();             //彈出一個使r為1的方案數
            if(c=='*')                          //&運算 
            {
                one.push((l1*r1%mod)%mod);      //兩個都為1 
                zero.push((l0*r0%mod+l1*r0%mod+l0*r1%mod)%mod);  //不都為1 
            }
            else                                //|運算 
            {
                zero.push((l0*r0%mod)%mod);     //兩個都為0 
                one.push((l0*r1%mod+l1*r0%mod+l1*r1%mod)%mod);   //不都為0 
            }
        }
    }
    printf("%d",zero.top()%mod);                //最后剩下的就是使整個表達式為0的方案數 
    return 0;
}

 


免責聲明!

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



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