去年參加了一次天梯賽,畢竟才大一,水平有限,L1 基本答了,L2 混了幾題部分正確,L3 題目還沒點開過。今年再戰,順便更新更新題解,也做本身複習用。另外,日常 HDU 的題目作多了,養成了動不動就用scanf
和printf
的習慣,其實 PTA 的題基本上用cin
和cout
都能過的,因此 下面的代碼中的全部c
語言部分爲了方便,均可以換成c++
,畢竟scanf
,printf
不只字母多,裏面還要打一堆%
太繁瑣了。
題解在不按期更新中,水平有限,最多也就作到 L2,歡迎在評論中提出問題,一塊兒討論。node
2020-01-09 更新: L1-015 ~ L1-017
2020-01-15 更新: L1-018 ~ L1-020
2020-01-20 更新: L2-002, L2-003
刷題連接 👉ios
一個「萬能頭文件」,基本上經常使用的 STL
都在裏面了,ACM 比賽就是怎麼高效怎麼來,這麼好用的東西不用白不用。c++
因爲 L1 題目廣泛簡單,因此備註比較少,後面的 L2 題目會適當加上備註。算法
pta 最難的一題!!!數組
#include <bits/stdc++.h> using namespace std; int main() { cout << "Hello World!"; return 0; }
由於圖形上下對稱,因此能夠先計算出上半圖形的高度、寬度,這題主要就是考察邏輯思惟。數據結構
#include <bits/stdc++.h> using namespace std; int main() { int n, height = 1, width = 1; char ch; cin >> n >> ch; --n; // 先扣去最中間的一個字符 while (true) { if (n - 2 * (width + 2) >= 0) { ++height; width += 2; n -= 2 * width; } else break; } for (int i = 0; i < height; ++i) { for (int j = 0; j < i; ++j) cout << ' '; for (int j = 0; j < 2 * height - 1 - 2 * i; ++j) cout << ch; cout << '\n'; } for (int i = 0; i < height - 1; ++i) { for (int j = 0; j < height - 2 - i; ++j) cout << ' '; for (int j = 0; j < 2 * (i + 1) + 1; ++j) cout << ch; cout << '\n'; } cout << n; return 0; }
雖然題目描述須要輸入正整數,可是輸入正整數須要一次一次取模才能獲得各位,直接以字符串的方式輸入就很簡單。post
#include <bits/stdc++.h> using namespace std; int main() { int cnt[10] = {0}; string num; cin >> num; for (int i = 0; i < num.size(); ++i) ++cnt[num[i] - '0']; for (int i = 0; i < 10; ++i) if (cnt[i] > 0) cout << i << ':' << cnt[i] << '\n'; return 0; }
沒有什麼難點,L1 不少題都像這樣。測試
#include <bits/stdc++.h> using namespace std; int main() { int F; cin >> F; int C = 5 * (F - 32) / 9; cout << "Celsius = " << C; return 0; }
把試機座位號看成下標,而後准考證號和考試座位號分別存入 a, b 數組,最後輸出就好。(准考證號 16 位,int 裝不下)優化
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL x, a[1005]; int n, m, y, z, b[1005]; int main() { scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%lld%d%d", &x, &y, &z); a[y] = x; b[y] = z; } scanf("%d", &m); for (int i = 0; i < m; ++i) { scanf("%d", &y); printf("%lld %d\n", a[y], b[y]); } return 0; }
用循環模擬出可能的連續因子序列。須要注意幾個優化的地方,for (int i = 2; i <= sqrt(n); ++i)
,很好理解,大於 sqrt(n)
的數再隨便乘一下就大於 n 了,確定就不是因子,因此這裏只走到 sqrt(n)
。ui
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL n, tmp; int start, len; int main() { scanf("%lld", &n); for (int i = 2; i <= sqrt(n); ++i) { tmp = 1; for (int j = i; tmp * j <= n; ++j) { tmp *= j; if (n % tmp) break; // 若是tmp不是n的因子,再乘任何數也不是 if (j - i + 1 > len) { start = i; len = j - i + 1; } } } if (!start) { start = n; len = 1; } printf("%d\n%d", len, start); for (int i = 1; i < len; ++i) printf("*%d", start + i); return 0; }
只要注意行尾不能有空格就行了。
#include <bits/stdc++.h> using namespace std; string a[] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"}; int main() { string s; cin >> s; if (s[0] == '-') cout << "fu"; else cout << a[s[0] - '0']; for (int i = 1; i < s.size(); ++i) cout << ' ' << a[s[i] - '0']; return 0; }
只能說 pta 的題太細節了,不難的一題可是正確率很低,由於很容易出現恰好 5 個數時,最後連換兩行的狀況。
#include <bits/stdc++.h> using namespace std; int main() { int a, b, sum = 0; scanf("%d%d", &a, &b); for (int i = a; i <= b; ++i) { sum += i; printf("%5d", i); if ((i - a + 1) % 5 == 0) printf("\n"); else if (i == b) printf("\n"); } printf("Sum = %d", sum); return 0; }
分數求和,難點就是通分,須要求最大公約數,能夠用歐幾里得算法(gcd)求得。最後還有一點須要注意,測試數據是在長整型範圍內的。
#include <bits/stdc++.h> typedef long long LL; using namespace std; LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a % b); } int main() { char c; int n; LL x, y, tmp; cin >> n; cin >> x >> c >> y; LL sx = x, sy = y; for (int i = 1; i < n; ++i) { cin >> x >> c >> y; tmp = sy / gcd(sy, y) * y; sx = sx * (tmp / sy) + x * (tmp / y); sy = tmp; } LL num = sx / sy; sx %= sy; tmp = gcd(sx, sy); sx /= tmp; sy /= tmp; if (sx == 0) cout << num; else if (num == 0) cout << sx << '/' << sy; else cout << abs(num) << ' ' << sx << '/' << sy; return 0; }
簡單的排個序。
#include <bits/stdc++.h> using namespace std; int main() { int a[3]; for (int i = 0; i < 3; ++i) cin >> a[i]; sort(a, a + 3); cout << a[0] << "->" << a[1] << "->" << a[2]; return 0; }
字符串,查找,刪除。
#include <bits/stdc++.h> using namespace std; int main() { string a, b; getline(cin, a); getline(cin, b); for (int i = 0; i < b.size();) { if (a.find(b[i]) != -1) a.erase(a.find(b[i]), 1); else ++i; } cout << a; return 0; }
水題。
#include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; cout << "2^" << n << " = " << pow(2, n); return 0; }
水題。
#include <bits/stdc++.h> using namespace std; int fac(int x) { return x == 1 ? 1 : fac(x - 1) * x; } int main() { int n, sum = 0; cin >> n; for (int i = 1; i <= n; ++i) sum += fac(i); cout << sum; return 0; }
水題三連。
#include <iostream> using namespace std; int main() { cout << "This is a simple problem."; return 0; }
主要就是四捨五入的部分,用二進制騷一下。
#include <bits/stdc++.h> using namespace std; int main() { int n; char c; cin >> n >> c; int m = (n & 1) ? n / 2 + 1 : n / 2; // 四捨五入 for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) cout << c; cout << endl; } return 0; }
水題,就是麻煩了點。
#include <bits/stdc++.h> using namespace std; int main() { int a[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; int b[] = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; int n; cin >> n; string s; bool flag = false; while (n--) { int sum = 0; cin >> s; for (int i = 0; i < s.size() - 1; ++i) sum += (s[i] - '0') * a[i]; sum %= 11; if (b[sum] != s.back()) { cout << s << endl; flag = true; } } if (!flag) cout << "All passed"; return 0; }
用 cout << setiosflags(ios::fixed) << setprecision(2)
控制小數點位數。
#include <bits/stdc++.h> using namespace std; int main() { string s; cin >> s; int cnt = 0; double flag = 1; if (s[0] == '-') { flag *= 1.5; s.erase(s.begin(), s.begin() + 1); } if ((s.back() - '0') % 2 == 0) flag *= 2; for (int i = 0; i < s.size(); ++i) if (s[i] == '2') ++cnt; cout << setiosflags(ios::fixed) << setprecision(2) << cnt * flag / s.size() * 100 << '%'; return 0; }
注意 12 點整不須要敲,過了 12 點就要敲。
#include <bits/stdc++.h> using namespace std; int main() { int h, m; scanf("%d:%d", &h, &m); if (h >= 0 && h < 12 || h == 12 && m == 0) printf("Only %02d:%02d. Too early to Dang.", h, m); else { if (m == 0) h -= 12; else h -= 11; while (h--) printf("Dang"); } return 0; }
這題比較坑,假如酒量給的是 1,須要喝 2 杯才能倒,因此輸入兩人的酒量後,都須要加 1。另外題目描述第二行輸入一個正整數 N,實際上 N 根本沒用。
#include <bits/stdc++.h> using namespace std; int main() { int n, m, _, a, b, c, d; int cnt1 = 0, cnt2 = 0; cin >> n >> m >> _; ++n, ++m; while (n && m) { cin >> a >> b >> c >> d; int tmp = a + c; if (b == tmp && d != tmp) { --n; ++cnt1; } else if (b != tmp && d == tmp) { --m; ++cnt2; } } if (n) cout << "B\n" << cnt1; else cout << "A\n" << cnt2; return 0; }
朋友圈人數若是爲 1,不作處理。大於 1 的話,說明有朋友,作個標記,注意輸出格式。
#include <bits/stdc++.h> using namespace std; int main() { int n, k, id; vector<bool> fri(100000, false), vis(100000, false); cin >> n; while (n--) { cin >> k; if (k == 1) cin >> id; else { while (k--) { cin >> id; fri[id] = true; } } } cin >> n; bool flag = false; while (n--) { cin >> id; if (!fri[id] && !vis[id]) { if (flag) cout << ' '; cout << setfill('0') << setw(5) << id; vis[id] = flag = true; } } if (!flag) cout << "No one is handsome"; return 0; }
就是幾個知識點,getline
前用 cin.get()
吸取回車,而後一些字符串基本操做,find
正向查找,rfind
反向查找,substr
切割。最後就是須要考慮到一種特殊狀況,逗號前的字符不足 3 個,那麼直接去使用 str.substr(p - 3, 3) == "ong"
判斷的話就會出現錯誤(最後一組測試數據),因此須要加上 p >= 3
這個前提條件。
#include <bits/stdc++.h> using namespace std; int main() { string str; int n; cin >> n; cin.get(); while (n--) { getline(cin, str); int p = str.find(','); if (p >= 3 && str.substr(p - 3, 3) == "ong" && str.substr(str.size() - 4, 3) == "ong") { p = str.size(); for (int i = 0; i < 3; ++i) p = str.rfind(' ', p - 1); cout << str.substr(0, p) << " qiao ben zhong.\n"; } else cout << "Skipped\n"; } return 0; }
複雜了一些的最短路,用優先隊列對查找最小dis
的過程作了優化。dis[i]
表示起點到i
的最短路長度,pre[i]
表示i
的前驅,cnt[i]
表示到達i
的最短路條數,sum[i]
表示最多的救援隊數目。
#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define N 505 using namespace std; int n, m, s, d, x, y, z; int num[N], path[N][N], dis[N], pre[N], cnt[N], sum[N]; bool vis[N]; struct Node { int key, value; Node(int k, int v) { key = k; value = v; } bool operator<(const Node &a) const { return value > a.value; } }; void dijkstra() { pre[s] = -1; cnt[s] = 1; sum[s] = num[s]; dis[s] = 0; priority_queue<Node> q; q.push(Node(s, 0)); while (!q.empty()) { int k = q.top().key; q.pop(); if (vis[k]) continue; vis[k] = true; for (int i = 0; i < n; ++i) { if (!vis[i]) { int cur_dis = dis[k] + path[k][i]; int cur_sum = sum[k] + num[i]; if (cur_dis < dis[i]) { dis[i] = cur_dis; pre[i] = k; cnt[i] = cnt[k]; sum[i] = cur_sum; } else if (cur_dis == dis[i]) { cnt[i] += cnt[k]; if (cur_sum > sum[i]) { sum[i] = cur_sum; pre[i] = k; } } q.push(Node(i, dis[i])); } } } printf("%d %d\n", cnt[d], sum[d]); stack<int> stk; int k = d; while (k != -1) { stk.push(k); k = pre[k]; } printf("%d", stk.top()); stk.pop(); while (!stk.empty()) { printf(" %d", stk.top()); stk.pop(); } printf("\n"); } int main() { memset(path, INF, sizeof(path)); memset(dis, INF, sizeof(dis)); scanf("%d%d%d%d", &n, &m, &s, &d); for (int i = 0; i < n; ++i) scanf("%d", &num[i]); for (int i = 0; i < m; ++i) { scanf("%d%d%d", &x, &y, &z); path[x][y] = path[y][x] = z; } dijkstra(); return 0; }
典型的空間換時間,若是按照是否重複而後一個一個刪除重複的,確定會超時。vis
數組作標記,沒重複就放到第一個隊列裏,重複的放到第二個隊列裏,最後讀一遍兩個隊列就好,複雜度差很少是 O(n)
#include <bits/stdc++.h> using namespace std; struct Node { int data, next; }; int head, n, a, b, c; vector<Node> node(100005); vector<bool> vis(10005); queue<int> q1, q2; int main() { scanf("%d%d", &head, &n); while (n--) { scanf("%d%d%d", &a, &b, &c); node[a].data = b; node[a].next = c; } for (int i = head; i != -1; i = node[i].next) { if (!vis[abs(node[i].data)]) { vis[abs(node[i].data)] = true; q1.push(i); } else q2.push(i); } int pos = q1.front(); q1.pop(); printf("%05d %d ", pos, node[pos].data); while (!q1.empty()) { pos = q1.front(); q1.pop(); printf("%05d\n%05d %d ", pos, pos, node[pos].data); } printf("-1\n"); if (!q2.empty()) { pos = q2.front(); q2.pop(); printf("%05d %d ", pos, node[pos].data); while (!q2.empty()) { pos = q2.front(); q2.pop(); printf("%05d\n%05d %d ", pos, pos, node[pos].data); } printf("-1"); } return 0; }
基本的結構體排序加貪心,可是這個 C++
裏的小數真是奇怪啊,不知道精度發生了什麼神奇的錯誤,下面第一個代碼提交一直有一組數據過不了,把 int
換成 double
就對了???。。。??
#include <bits/stdc++.h> using namespace std; struct Node { int x, y; double z; bool operator<(const Node &node) const { return z > node.z; } } a[1005]; int n, d; int main() { cin >> n >> d; for (int i = 0; i < n; ++i) cin >> a[i].x; for (int i = 0; i < n; ++i) { cin >> a[i].y; a[i].z = a[i].y * 1.0 / a[i].x; } sort(a, a + n); double sum = 0; for (int i = 0; i < n; ++i) { if (d >= a[i].x) { d -= a[i].x; sum += a[i].y; } else { sum += d * a[i].z; break; } } cout << fixed << setprecision(2) << sum; return 0; }
正確代碼,太坑了吧:
#include <bits/stdc++.h> using namespace std; struct Node { double x, y, z; // x, y 換成 double bool operator<(const Node &node) const { return z > node.z; } } a[1005]; int n; double d; // d 換成 double int main() { cin >> n >> d; for (int i = 0; i < n; ++i) cin >> a[i].x; for (int i = 0; i < n; ++i) { cin >> a[i].y; a[i].z = a[i].y / a[i].x; } sort(a, a + n); double sum = 0; for (int i = 0; i < n; ++i) { if (d >= a[i].x) { d -= a[i].x; sum += a[i].y; } else { sum += d * a[i].z; break; } } cout << fixed << setprecision(2) << sum; return 0; }
本覺得上完數據結構的課應該就會各類樹了,結果發現本身仍是太年輕。這題按照二叉搜索樹的性質建樹,先試試是否是二叉搜索樹,再試試是否是二叉搜索樹的「鏡像」,若是最後能建成就順便輸出後序遍歷。
#include <bits/stdc++.h> using namespace std; int n, pre[1005], post[1005], cnt = 0; bool flag; void build(int l, int r) { if (l > r) return; int i = l + 1, j = r; if (!flag) { while (i <= r && pre[i] < pre[l]) ++i; while (l < j && pre[j] >= pre[l]) --j; } else { while (i <= r && pre[i] >= pre[l]) ++i; while (l < j && pre[j] < pre[l]) --j; } if (i - j != 1) return; build(l + 1, j); build(i, r); post[cnt++] = pre[l]; } int main() { scanf("%d", &n); for (int i = 0; i < n; ++i) scanf("%d", &pre[i]); build(0, n - 1); if (cnt != n) { flag = true; cnt = 0; build(0, n - 1); } if (cnt != n) printf("NO\n"); else { printf("YES\n"); printf("%d", post[0]); for (int i = 1; i < cnt; ++i) printf(" %d", post[i]); printf("\n"); } return 0; }
馬拉車算法模板題,c(center)表示中間點,r(right)表示迴文串的右邊界。具體算法思路不說了,還有須要理解的就是 r 關於 c 的對稱點爲2 * c - i
。
#include <bits/stdc++.h> using namespace std; int manacher(string s) { string str = "$#"; for (int i = 0; i < s.size(); ++i) { str.push_back(s[i]); str.push_back('#'); } int c = 0, r = 0; vector<int> p(str.size()); for (int i = 1; i < p.size(); ++i) { p[i] = r > i ? min(p[2 * c - i], r - i + 1) : 1; while (str[i - p[i]] == str[i + p[i]]) ++p[i]; if (r - c + 1 < p[i]) { c = i; r = i + p[i] - 1; } } // return s.substr((2 * c - r) / 2, r - c); return r - c; } int main() { string s; getline(cin, s); cout << manacher(s); return 0; }
結構體排序,輸入的錢是分爲單位,最後輸出時除以 100.0 獲得元。用結構體儲存每一個人的編號,收入,搶到紅包數。
#include <bits/stdc++.h> #define N 10005 using namespace std; struct Node { int key, sum, cnt; Node() : sum(0), cnt(0) {} } a[N]; int n, k, x, y; bool cmp(Node a, Node b) { if (a.sum == b.sum) { if (a.cnt == b.cnt) return a.key < b.key; return a.cnt > b.cnt; } return a.sum > b.sum; } int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) { a[i].key = i; scanf("%d", &k); while (k--) { scanf("%d%d", &x, &y); a[i].sum -= y; a[x].sum += y; ++a[x].cnt; } } sort(a + 1, a + n + 1, cmp); for (int i = 1; i <= n; ++i) printf("%d %.2f\n", a[i].key, a[i].sum / 100.0); return 0; }
並查集,若是兩我的是朋友關係就合併,不然,用flag
數組標記兩人是敵人。最後直接判斷一下輸出就好。
#include <bits/stdc++.h> #define N 105 using namespace std; int n, m, k, x, y, z; int f[N], _rank[N] = {0}, flag[N][N] = {false}; int _find(int x) { return x == f[x] ? x : f[x] = _find(f[x]); // 路徑壓縮 } void _union(int x, int y) { int f1 = _find(x), f2 = _find(y); if (f1 != f2) { if (_rank[f1] < _rank[f2]) // 按秩合併 f[f1] = f2; else { f[f2] = f1; if (_rank[f1] == _rank[f2]) ++_rank[f1]; } } } int main() { scanf("%d%d%d", &n, &m, &k); for (int i = 1; i <= n; ++i) f[i] = i; while (m--) { scanf("%d%d%d", &x, &y, &z); if (z == 1) _union(x, y); else flag[x][y] = flag[y][x] = true; } while (k--) { scanf("%d%d", &x, &y); if (_find(x) == _find(y)) { if (!flag[x][y]) printf("No problem\n"); else printf("OK but...\n"); } else { if (!flag[x][y]) printf("OK\n"); else printf("No way\n"); } } return 0; }
肝了我一晚,怎麼提交都是部分正確,我是真的無奈,直到看到有個題解:「用線性調整 heap 的話是不對的,題目要求是一個一個插入空的 MinHeap,因此只能插入,上浮,插入,上浮,插入,上浮。。。」,然而我試了一晚線性建堆。
思路很粗暴,先建堆(思路就是上面那句話,採用上浮的方式,加一個數浮一次),而後用 stringstream
把單詞分割出來裝進 vector
,剩下就是簡單判斷。
#include <bits/stdc++.h> #define N 1005 using namespace std; int n, m, a[N]; string s; stringstream ss; vector<string> v; void swim(int i) { int parent = (i - 1) / 2; if (parent >= 0 && a[parent] > a[i]) { swap(a[i], a[parent]); swim(parent); } } int toInt(string &s) { stringstream ss; ss << s; int x; ss >> x; return x; } int main() { ios::sync_with_stdio(false); cin >> n >> m; for (int i = 0; i < n; ++i) { cin >> a[i]; swim(i); } cin.get(); // 吸取回車 for (int i = 0; i < m; ++i) { getline(cin, s); ss << s; while (ss >> s) v.push_back(s); if (v[3] == "root") { if (toInt(v[0]) == a[0]) cout << "T\n"; else cout << "F\n"; } else if (v[4] == "siblings") { int p1 = find(a, a + n, toInt(v[0])) - a; int p2 = find(a, a + n, toInt(v[2])) - a; if (((p1 - 1) >> 1) == ((p2 - 1) >> 1)) cout << "T\n"; else cout << "F\n"; } else if (v[3] == "parent") { int p1 = find(a, a + n, toInt(v[0])) - a; int p2 = find(a, a + n, toInt(v.back())) - a; if ((p2 - 1) >> 1 == p1) cout << "T\n"; else cout << "F\n"; } else { int p1 = find(a, a + n, toInt(v[0])) - a; int p2 = find(a, a + n, toInt(v.back())) - a; if ((p1 - 1) >> 1 == p2) cout << "T\n"; else cout << "F\n"; } v.clear(); ss.clear(); } return 0; }