藍橋杯[十二屆][B組]-括號序列
2022-03-29 00:57 幻霞 閱讀(430) 評論(0) 收藏 舉報題目來自藍橋杯練習系統

代碼鏈接:https://blog.csdn.net/yanweiqi1754989931/article/details/123093179
這一題在思路不清楚的情況下相當難理解和解決,包括代碼,解的話一開始筆者就沒什么思路,想出來的方案要么超時要么難以操作
附上代碼和解析的思路,希望能幫到和筆者一樣被嚴重困擾的朋友
#include <bits/stdc++.h> #define int long long using namespace std; // 最大長度和模 const int N = 5010, MOD = 1e9 + 7; // dp[i][j]代表到第i個符號, 左括號比右括號多i個的添加方案(添加的是左括號) int dp[N][N]; inline int calc(string s) { // 長度 int len = s.size(); // 初始化 memset(dp, 0, sizeof dp); // 讀到位置為0時左括號比右括號多0個的解決方案數量為1 dp[0][0] = 1; // 依次枚舉每個位置 for(int i = 1; i <= len; i++) { // i-1轉換為索引 if(s[i - 1] == '(') { // 依次左比右多1~len個的情況 for(int j = 1; j <= len; j++) { // 繼承上一個的狀態,因為讀入的是左括號,所以不能加左括號了 dp[i][j] = dp[i - 1][j - 1]; } } else { // 這里處理了j=0的情況,在下面的循環中如果j=0,j-1會越界
// 其實這里表示的情況是到第i個符號,左括號小于等于右括號的情況),()), ,(())這類情況
// 此時的方案數=上一個相同情況下的方案數+左括號多一個的狀態(多的狀況是去補全當前添加的右括號)
// 比如,空 ,(),,())
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % MOD;
// 如果為右括號,考慮添加左括號
// dp[i][j-1]是dp[i][0]~dp[i][j-1]的和,代表了之前的有效解集(其實j要小于i/2 否則你會發現它的邏輯意義很奇怪,只不過這里給它延伸了一下),同時,
// dp[i][0] 到dp[i][j-1]的和其實后面的沒有什么意義,只是輔助運算,從結果看我們只取了第一個不為0的解,也就是說只要計算的dpij第一個不為0(即dp[i-1][0]和dp[i-1][1])
// dp[i-1][j+1]是在上一次的情況下,模擬左括號多一個的情況,這樣剛好和當前的右括號構成合法解 for(int j = 1; j <= len; j++) { dp[i][j] = (dp[i - 1][j + 1] + dp[i][j - 1]) % MOD; } } } // 調試的輸出 cout<<s<<endl; cout<<"j: "; for(int j=0;j<=len;j++){ printf("%4lld ",j); } cout<<endl; for(int i = 0; i <= len; i++) { cout<<i<<": "; for(int j = 0; j <= len; j++) { printf("%4lld ",dp[i][j]); } cout<<endl; } for(int i = 0; i <= len; i++) { // 返回第一個不為零的值,理論上不可能是無解的,為什么取第一個不為0的是因為 存在((((這種情況 // dp4 0 的值為0的,也就是不能再加左括號了 if(dp[len][i]) return dp[len][i]; } // 否則返回負一 return -1; } // inline void solve() { // 輸入 string s; cin >> s; // 鏡像一個 string ss = s; // 翻轉鏡像 reverse(ss.begin(), ss.end()); // 遍歷每一個位置,如果是左括號就變成右括號,如果是右括號就變為左括號,相當于翻轉 for(int i = 0; i < ss.size(); i++) { ss[i] = (ss[i] == '(') ? ')' : '('; } // 最終解為兩種情況乘積對mod取模 :ss其實是s的鏡像比如s:(((),ss:)(((->)))( // 乘積的原理:形如)(((,此時必然要添加一個左括號以補全左邊的右括號,只有一種補全方案,所以1*,而添加右括號的方式有5種,所以是5* // 所需要添加的左右括號方案便是1*5 cout << calc(s) * calc(ss) % MOD << endl; } signed main() { solve(); return 0; }
筆者回來復習,叒叒叒叒叒叒叒看不懂代碼了。
但是隱約記得一些東西,現在討論一下這個
)(((
注釋中說添加右括號的方案數為5,這個比較好解釋
首先它的方案數等價于(((,因為左邊必須要有一個()
那么(((的方案數,(時為1,((時為()(),(()),
(((時為 1.()()(),2.()(()) 3.(())() 4.((())),5.(()())
在這些左邊再加一個()結果還是5個,所以這里討論的是加右括號的方案數(當然在函數里是翻轉考慮左括號的)
這里翻轉一下變成)))(
浙公網安備 33010602011771號