去年参加了一次天梯赛,毕竟才大一,水平有限,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; }