本文为PAT甲级真题中的1001 - 1005题的详细题解,本文的解法仅个人的思路和解法,有什么更优解欢迎讨论(AI一键生成答案的年代还能坚持写题解吗?)
题目链接:真题练习
题目1001: A+B Format
题目大意:
给你两个整数a,b,范围是[-1e6, 1e6]
现在算出a + b,但是答案得从最低位开始,每三位加一个逗号
比如输入-1000000 9
输出-999,991
解题思路:
其实这个题目就很简单了,首先注意题目范围,1e6,所以加起来也就2e6,最多两个逗号
我们可以把和sum = a + b转换成字符串
实现的时候可以直接用insert方法来实现,具体实现细节可以看代码,注意负号就行了,可以先输出负号,然后把sum变成正数处理。
代码(C++):
cpp
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int a, b;
std::cin >> a >> b;
int sum = a + b;
if (sum < 0) {
std::cout << "-";
sum = -sum;
}
std::string s = std::to_string(sum);
int sz = s.size();
if (sz > 3) {
s.insert(sz - 3, ",");
}
if (sz > 6) {
s.insert(sz - 6, ",");
}
std::cout << s;
}
题目1002: A+B for Polynomials
题目大意:
现在有两个一元多项式A和B, 现在给出多项式的指数和系数
现在求出两个多项式相加的结果
解题思路:
这题我的做法是,由于指数的大小最多是1000,指数的范围是[0, 1000],所以我们可以建立一个长度为1001的数组用来记录每一个指数的系数
std::vector<double> p(1001)
然后输入两次即可,每次输入的时候,把对应位置的系数相加
p[n] += a;
然后题目中说需要输出个数,那我们可以用一个答案数组std::vector<std::pair<int, double>> ans、
注意,系数可能是负数(这个题目中好像没有详细说明),所以我们需要把数组p中绝对值大于0的元素放在答案数组里面。
具体实现细节可以参考代码
代码(C++):
cpp
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::vector<double> p(1001);
for (int t = 0; t < 2; t++) {
int k;
std::cin >> k;
for (int i = 0; i < k; i++) {
int n;
double a;
std::cin >> n >> a;
p[n] += a;
}
}
//{系数, 指数}
std::vector<std::pair<double, int>> ans;
for (int i = 1000; i >= 0; i--) {
//fabs()用于把double类型的变量取绝对值
if (fabs(p[i]) > 0) {
ans.push_back({p[i], i});
}
}
std::cout << ans.size();
for (auto& [q, n] : ans) {
//可以在前面先输出一个空格,这样就解决了结尾空格的问题
std::cout << " " << n << " ";
std::cout << std::fixed << std::setprecision(1) << q;
}
}
题目1003:Emergency
题目大意:
现在有一个无向图,无向图每个节点表示一个城市,每个城市有若干个救援队员,每到一个城市就会召集这个城市的所有救援队员。
现在从某个节点c1出发,到达c2。保证c1到c2至少有一条路径,现在需要求出c1到c2的最短路径的数量,以及能得到的最大救援队员数量。
解题思路:
比较基础的dijstra算法模板题,这里没有太多细节,注意实现思路就好。
首先是我们要知道最短路线数量,我们可以建立一个数组std::vector<int> road(n, 0),road[u]表示从起点s到u的最短路线数量
还需要知道最大救援队的数量,那么我们可以建立一个数组std::vector<int> cnt(n, 0), cnt[u]表示从起点s到u,最短路径的同时的最大救援队数量。
现在进行dijstra算法,设此时的节点是u, 遇到一个新节点v,有两种情况:
情况1:最短路长度更小,那么我们的road[v] = road[u],找到了更短的路(或者说找到了新节点),直接继承road[u]。
救援队数量cnt[v] = cnt[u] + team[v],遇到了更短的路线,那么直接加上次节点的救援队数量即可
情况2:最短路长度相同,那么走到节点v的最短路线数量要加上走到u的最短路线数量。cnt[v] += cnt[u]
我们目标是要获取尽可能多的救援队数量,我们需要比较一下两种路线的救援队数量,新节点v的救援队数量是cnt[v] ,如果我们从u走到v,救援队数量是cnt[u] + team[v],比较一下这两个即可
具体实现细节可以参考代码~
代码(C++):
cpp
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, s, t;
std::cin >> n >> m >> s >> t;
struct E {
int to, w;
};
std::vector<int> team(n);
for (int& x : team) {
std::cin >> x;
}
std::vector<std::vector<E>> g(n);
for (int i = 0; i < m; i++) {
int u, v, w;
std::cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
std::vector<i64> road(n, 0); //从起点s到节点u的最短路径数量road[u]
std::vector<int> cnt(n, 0); //从起点s到节点u,最短路的情况下,最大的救援队数量cnt[u]
std::vector<int> dist(n, 2e9); //dist[u]表示从起点s出发到节点u的最短距离
using P = std::pair<int, int>; //{d, u}到节点u距离d
std::priority_queue<P, std::vector<P>, std::greater<P>> pq;
dist[s] = 0;
road[s] = 1;
cnt[s] = team[s];
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) {
continue;
}
for (auto [v, w] : g[u]) {
int nd = dist[u] + w;
if (nd < dist[v]) {
dist[v] = nd;
road[v] = road[u];
cnt[v] = cnt[u] + team[v];
pq.push({nd, v});
} else if (nd == dist[v]) {
road[v] += road[u];
int cur = cnt[u] + team[v];
if (cur > cnt[v]) {
cnt[v] = cur;
}
}
}
}
std::cout << road[t] << " " << cnt[t];
}
题目1004:Counting Leaves
题目大意:
现在给你一颗树,从根节点开始,一层一层的往下统计,统计每一层中没有孩子节点的节点数量
解题思路:
BFS的基础题,使用BFS从根节点开始一层一层的往下进行遍历。
具体实现的时候,我们可以建立一个**std::queue<std::pair<int, int>> q;**用来存节点和此时的深度d。
在遍历的过程中,如果此节点u的g[u]是空的,就表示没有孩子节点。BFS遍历的过程中,如果发现深度发生变化,那么就记录此时的没有孩子节点的节点数量,存在答案数组里面。
具体实现细节可以查看代码
代码(C++):
cpp
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int>> g(100);
for (int i = 0; i < m; i++) {
std::string s;
std::cin >> s;
int ID = std::stoi(s);
int k;
std::cin >> k;
for (int j = 0; j < k; j++) {
std::cin >> s;
g[ID].push_back(std::stoi(s));
}
}
std::vector<int> ans;
std::queue<std::pair<int, int>> q;
q.push({1, 1}); //{1, 1}表示一开始从根节点1开始遍历,开始的时候深度是d
int cnt = 0; //没有孩子节点的节点数量
int lv = 1; //层
while (!q.empty()) {
auto [u, d] = q.front();
q.pop();
if (d != lv) { //层发生了变化
ans.push_back(cnt);
lv++;
cnt = 0;
}
if (g[u].empty()) {
cnt++;
}
for (auto v : g[u]) {
q.push({v, d + 1});
}
}
//最后一层也要添加进来
ans.push_back(cnt);
for (int i = 0; i < ans.size(); i++) {
std::cout << ans[i];
if (i != ans.size() - 1) {
std::cout << " ";
}
}
}
题目1005:Spell It Right
题目大意:
给出一个数字N,这个N的范围是[0, 1e100]
求出N的各个位数和,答案用英文表示出来
比如输入12345,位数和为15,你需要把答案表示成one five
解题思路:
这个题首先就可以用字符串存数字,然后把各个位数和加起来,设置为sum
那我们怎么快速的把答案用英文表示出来呢?
我们把sum再转换成成字符串,建立一个数组std::vector<std::string> arr = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
然后就快速实现,具体实现细节可以看代码
代码(C++):
cpp
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string s;
std::cin >> s;
int sum = 0;
for (char& c : s) {
sum += c - '0';
}
std::vector<std::string> arr = {
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine"
};
std::string ans = "";
for (char c : std::to_string(sum)) {
int num = c - '0';
ans += arr[num];
ans += " ";
}
ans.pop_back();//把最后一个空格删掉
std::cout << ans;
}