\(dp[i][j]\) 表示到第 \(i\) 行時,縮進次數爲 \(j\) 的方案數。python
#include<bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; string c; long long dp[5555][5555]; int main() { int n; cin >> n; dp[0][0] = 1; for (int i = 0; i < n; i++) { cin >> c; if(c == "f") { for (int j = 1; j < n; j++) { dp[i + 1][j] = dp[i][j - 1]; } } else { for (int j = n; j >= 0; j--) { dp[i + 1][j] = (dp[i][j] + dp[i + 1][j + 1]) % MOD; } } } cout << dp[n][0] << endl; return 0; }
將字符串按字符分組,記錄字符和連續相同的字符個數,那麼每次刪掉的字符應該是,左右兩邊的組中的一個字符,中間每組兩個字符 ,將空組刪掉,合併能夠合併的組。模擬一下就好了,由於對於每組每回合至少會刪掉一個字符,複雜度 \(O(n)\)。c++
#include<bits/stdc++.h> using namespace std; string s; vector<pair<char, int> > v, tmp; int main() { cin >> s; int len = s.length(); v.push_back(pair<int, int>(s[0], 1)); for (int i = 1; i < len; i++) { if(s[i] == v.back().first) { v[v.size() - 1].second++; } else { v.push_back(pair<int, int>(s[i], 1)); } } int ans = 0; while(v.size() > 1) { ans++; for (int i = 0; i < v.size(); i++) { if(!i) { if(v[i].second - 1 > 0) { v[i].second--; tmp.push_back(v[i]); } } else if(i == v.size() - 1) { if(v[i].second - 1 > 0) { v[i].second--; if(!tmp.empty() && tmp.back().first == v[i].first) { tmp[tmp.size() - 1].second += v[i].second; } else { tmp.push_back(v[i]); } } } else { if(v[i].second - 2 > 0) { v[i].second -= 2; if(!tmp.empty() && tmp.back().first == v[i].first) { tmp[tmp.size() - 1].second += v[i].second; } else { tmp.push_back(v[i]); } } } } v = tmp; tmp.clear(); } cout << ans << endl; return 0; }
與前面兩題相比感受這道題反而沒什麼技巧。spa
找入度爲 0 的點,分類存儲,遍歷再去 DFS 更新其它點的入度,重複這樣便可。code
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; vector<int> G[N]; int c[N], d[N]; vector<int> v0, v1; void dfs(int flg, int u) { for (int v : G[u]) { d[v]--; if (!d[v]) { if (c[v]) { if (flg) dfs(flg, v); else v1.push_back(v); } else { if (flg) v0.push_back(v); else dfs(flg, v); } } } } int main() { int n, m; cin >> n >> m; for (int i = 0; i < n; i++) { cin >> c[i]; } for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; G[v].push_back(u); d[u]++; } for (int i = 0; i < n; i++) { if (!d[i]) { c[i] ? v1.push_back(i) : v0.push_back(i); } } int ans = 0; while (!v0.empty() || !v1.empty()) { for (int u : v0) { dfs(0, u); } v0.clear(); if (!v1.empty()) ans++; for (int u : v1) { dfs(1, u); } v1.clear(); } cout << ans << endl; return 0; }
構造題。ci
對二進制和位運算仍是不夠敏感啊。字符串
對於第一個問題,要求排列後每一個數都不在原來的位置,且與位置序號數按位與運算後都爲 0 。get
對於奇數狀況,無解,由於奇數狀況下全部數表示成二進制後,末尾爲 \(1\) 的數個數必定比爲 \(0\) 的多 \(1\) ,因此不管如何,都會存在一個對應狀況按位與後末尾爲 \(1\) 。string
對於偶數狀況,只須要注意到一個事實:it
若 \(n=2^k\) ,\(m=2^k-1\),\((n+i)\&(m-i)=0(m \geq i)\) ,由於此時 \(n\) 除了前導 \(1\) 全是 \(0\) ,\(m\) 除了前導 \(0\) 之外全是 \(1\) ,那麼能夠想象成這些 \(1\) 全均可以加到 \(n\) 上去。io
舉個例子,若 \(n=12\),咱們找到小於等於 \(n\) 的最大的 \(2\) 的倍數 \(8\) ,那麼 \((7,8)\ (6,9)\ (5,10)\ (4,11)\ (3,12)\) 對應,而後 \(n=2\),咱們找到小於等於 \(n\) 的最大的 \(2\) 的倍數 \(2\),\((1,2)\) 對應,\(n=0\) 後能夠發現全部數都有對應關係了。由於起始 \(n\) 是偶數,且每次減去的數必定是偶數,且 \(n\) 必定不會減到小於 \(0\),因此必定能構造出答案。
對於第二個問題,按位運算後都不爲 \(0\) 。
若是 \(n\) 是 \(2\) 的倍數,則無解,由於沒有數能夠和 \(n\) 對應。
對於 \(n<8\) 的狀況要特判。
對於 \(n\geq 8\) 的狀況,考慮 \([8,15]\ [16, 31]...\),對於每一個區間單獨考慮,轉化成二進制數後每一個數的首位數必定都爲 \(1\) ,只要保證每一個數都不在原來的位置便可。
#include <bits/stdc++.h> using namespace std; int p2(int x) { int a = 1; while (a * 2 <= x) { a *= 2; } return a; } int ans[111111]; int main() { int n; cin >> n; if (n & 1) { cout << "NO\n"; } else { cout << "YES\n"; int m = n; while (m) { int x = p2(m); for (int i = x, j = x - 1; i <= m; i++, j--) { ans[i] = j; ans[j] = i; } m -= 2 * (m - x + 1); } for (int i = 1; i <= n; i++) { cout << ans[i] << " \n"[i == n]; } } if (n <= 5 || p2(n) == n) cout << "NO\n"; else { cout << "YES\n"; if (n == 6) cout << "3 6 2 5 1 4\n"; else if (n == 7) cout << "7 3 6 5 1 2 4\n"; else { cout << "7 3 6 5 1 2 4"; int l = 8; while (l <= n) { int r = min(n, l * 2 - 1); for (int i = l + 1; i <= r; i++) { cout << " " << i; } cout << " " << l; l = r + 1; } cout << endl; } } return 0; }