合法括號序列(dp+組合數學)


鍵盤上有左括號(,右括號),和退格鍵-,共三個鍵。

牛牛希望按鍵n次,使得輸入的字符串恰好一個合法的括號序列。

每按一次左括號(,字符串末尾追加一個左括號(

每按一次右括號),字符串末尾追加一個右括號)

每按一次退格鍵-,會刪掉字符串的最后一個字符,

特別的,如果字符串為空,牛牛也可以按退格,但是什么都不會發生。

輸出方案數對p取模,注意p可能不是質數。

注:只要按鍵方法不同,就是不同的方案,即使得到的序列一樣。

Solution

這題和其他關於括號序列的題不太一樣,因為有了刪除操作。

由於我太ZZ,然后搞了個*b方程。

設dp[i][j]表示按了i次,造出了一個長度為j的序列的方案數。

轉移是dp[i][j]=dp[i-1][max(0,j-1)]+dp[i-1][j+1]

這里取max是指當序列為空時我也可以退格。

然后我們就有了一個長度為j的序列,我們要把左括號和右括號往里放,使得它是一個合法的括號序列。

其實這個方案數就是卡特蘭數。

於是我們枚舉長度,答案加上dp[n][i]*catalan(i/2)。

然后樣例爆炸。

發現(+del和)+del最后制造出的序列是一樣的,但是是兩種不同的方案,但按照上面的方法dp只會計算一次。

所以刪除時要考慮刪什么字符。

正確轉移方程:dp[i][j]=dp[i-1][max(0,j-1)]+dp[i-1][j+1]*2.

Code

#include<iostream>
#include<cstdio>
#define N 1002 
using namespace std;
typedef long long ll;
int n;
ll f[N][N],dp[N][N],p,ans;
int main() {
    scanf("%d%lld",&n,&p);
    dp[0][0]=1;
    for(int i=1;i<=n;++i)
     for(int j=0;j<=i;++j)
       dp[i][j]=(dp[i-1][max(j-1,0)]+2*dp[i-1][j+1])%p;
    f[0][0]=1;
    for(int i=1;i<=n;++i) {
       f[i][0]=f[i-1][1];
       for(int j=1;j<=i;++j)
         f[i][j]=(f[i-1][j+1]+f[i-1][j-1])%p;
    }
    for(int i=0;i<=n;i+=2)(ans+=(f[i][0]*dp[n][i])%p)%=p;
    printf("%lld\n",ans);
    return 0;
}

 


免責聲明!

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



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