[AHOI2017初中組]rexp

來自洛谷的一道綠題:
rexp
題目描述
給出一個由(,),|,a組成的序列,求化簡後有多少個a。

化簡規則:

1、形如aa…a|aa…a|aa…a的,化簡結果爲“|”兩邊a的個數最多的一項,例如a|aa|aaa=aaa
2、先算帶括號的序列,例如(a|a)|aaa=aaa

輸入輸出格式
輸入格式:
一行一個序列

輸出格式:
化簡後a的個數

輸入輸出樣例
輸入樣例#1:
aa(aa)|(aa|(a|aa))aa
輸出樣例#1:
4

序列長度不超過100000
保證序列合法且括號內和“|”左右均非空

題目分析:
這道題很明顯是一道遞歸題,所謂的遞歸思想無非是化繁爲簡,在這道題哩是有很好的體現的

首先,題意是很好理解的,無非是噁心至極的化簡
我們可以分開來解決

首先我們先將問題想象成只有 ‘|’ 和 ‘a’ 的字符串,這樣的話,我們要去完成的無非是不斷地比較大小了
這樣的話,完全可以交由循環或遞歸來解決了
但是我們此處的是一個大問題中的小分支,用遞歸來解決的話更富有兼容性
此處來講解一下子問題的解決方法:
我們可以用 ‘|’ 來劃分遞歸的層數,無非是遇到這個符號就向深層次遞歸
每一次遞歸就相當於在統計一次當前的左邊,到底了,也就是到了最後一個做左邊了,那麼就是將左邊化爲右邊了,開始比較,知道判斷到遞歸起點時,返回最大的值,假如當前的左邊 ‘a’ 的個數是ans1,那麼找到第一個 ‘|’ 我們就要換一個左邊了——開始統計ans2,依次類推,最後的話便是上面說的了——“回放”——ansn與ansn-1比較。。。ans2與ans1比較,每一次向後退的,都是局部的最大的

這裏給出部分代碼:

int work(int ans)
{
    char c;
    while(~scanf("%c",&c))
    {
        if (c == 'a') ans++;
    
        if (c == '|') 
		{
			ans  =  max(ans,work(0));     
			
			break;
		}
    }
    
    return ans;
}

這裏解釋一下上文的break——直接跳出,因爲這裏的遞歸是不停向前的,上面的部分自然會不停重複,也就可以讀進來,同時,根據遞歸的性質是到底才反返回的,中途也是在不停的比較大小的,所以當完全到邊上的時候,也就是到第一次向前挖的時候,解就有了 (在上面寫註釋要向右拉,MD太長了,或者是我太迂了,反正好醜,寫下面吧)

之後便是我們激動人心的處理噁心括號的階段了

首先是左括號,據我們所知,我們是要先迭代 (嘿嘿是不是很高級的樣子,其實就是計算) 括號裏的,而括號往往時同前面的相連接的,左括號前是 ‘a’ 必然是接上去的,左括號前是 ‘|’ 的話,因爲我們在上面已經進行過處理了——每一次遇到 ‘|’ 就重新統計,在本質上面,同 ‘a’ 性質是一樣的,只不過是一個空的,要重新算的而已

之後便是右括號了,由上可知,我們沒一次遇到左括號時,都是重新開一個來計算其內部的,當然,等到第一個右括號時,我們要做的便只是返回了——這是一個邊界!——因爲右括號的到來,就是意味着我們所構思的一個的遞歸的層結束,之後便交回電腦——層層遞歸回去了

需要添加的代碼無非是:

        if (c == '(') ans =  ans + work(0);

        if (c == ')') return ans;

到這裏,這道題就完結了,GG

完整代碼:

#include <bits/stdc++.h>
using namespace std;
int work(int ans)
{
    char c;
    while(~scanf("%c",&c))
    {
        if (c == 'a') ans++;
    
        if (c == '(') ans =  ans + work(0);
    
        if (c == '|') 
		{
			ans  =  max(ans,work(0));
			
			break;
		}
    
        if (c == ')') return ans;
    }
    
    return ans;
}
    

int main()
{	
    cout<<work(0);
    
    return 0;
}

註釋懶得寫了,上面說的很詳細,多看看,多想想,就OK了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章