21秋季PAT甲级满分总结
1.考前准备
我的考试准备经历可能比较特别,今年3月份花了1个月在MOOC看了翁老师的C语言课以及浙大的数据结构与算法分析课程,之前的基础就是大一学过C语言课,如今已经毕业一年了,浙大的数据结构网课是纯用C实现的,所以做起题来比较繁琐,像队列堆栈这种简单的线性结构也要自己实现,不过好处也是很明显的,手动实现多了就会对数据结构有更深的了解,也算是打下了比较好的基础吧。
网课看到哈希映射那里就没继续了,因为4月份有一些工作上的事情,结果这一搁置就到了9月份(对),我本来报名只是想参与一下,9月初甚至还想退款但是发现报名截止前几天不接受退款了,于是赶紧买了算法笔记和上机实战指南,等快递的时间把浙大网课哈希映射的部分看完了,大概4号书到了就开始补STL容器部分,从这时候开始练习用C++做题。
4号到10号基本就是在看书,题都没时间做,把以前的笔记拿出来配合算法笔记一起重温,发现浙大的网课内容还是有一点不足的,比如普通树的存储,最短路的dilkstra + DFS就没讲;但是算法笔记一些内容也没有网课讲的明白,像dilkstra还是陈老师直接演示比较直观,一下就明白了,光看文字确实太枯涩了。
看书一直持续到11号早上,11号下午就是考试时间了,说实话我心里还是很虚的,因为基本上就没怎么刷题,3月份上网课的时候会去做课后的作业,难度其实和PAT甲级一样,网课的内容十分精炼,上几十分钟网课就要去做PAT练习,可想而知有多困难,那时候一道题卡一天是很正常的事情,9月份以来代码量主要集中在测试上,比如动手实现一下STL容器和相关算法,或者自己建一棵树遍历一下,考完试看了一下PTA网站的做题量,PAT难度的题只有25道,还是3月份做的。
2.考试经历(附代码)
2.1第一题
#include <cstdio>
using namespace std;
typedef struct Node ar;
struct Node {
int adr;
int num;
};
int main() {
int arrNum, queries, idxSum = 0, len = 0, idx, decNum = 1;
scanf("%d%d", &arrNum, &queries);
ar t[arrNum];
for (int i = 0; i < arrNum; i++) {
scanf("%d %d", &t[i].adr, &t[i].num);
len += t[i].num;
// printf("len = %d\n", len);
}
for (int i = 0; i < queries; i++) {
scanf("%d", &idx);
// printf("%d ", idx);
if (idx >= len) {
printf("Illegal Access\n");
} else {
int j = 0, tmpNum = 1;
while (idx >= t[j].num) {
idx -= t[j].num;
tmpNum++;
j++;
}
if (tmpNum > decNum) decNum = tmpNum;
printf("%d\n", t[j].adr + idx*sizeof(int));
}
}
printf("%d", decNum);
return 0;
}
题目忘记摘了,等真题出来以后大家再自己对着看吧,题目大意其实就是把一个数组分成n段,每段有自己的初始地址和长度,比如第1段A1从1024开始,长度为5;第2段A2从2048开始,长度为4;那么给出一个下标7,则A[7] = A2【2】 = 2048 + 2 * sizeof(int) = 2056;
是不是很简单?但是原题真的很难读,我第一时间根本没明白是啥意思,这题花了大概五十多分钟,可以说是开门不顺了,后来做完以后,发现这题的通过率是最低的,我感觉难度主要在理解题意上了。
2.2第二题
#include <cstdio>
#include <stack>
#include <algorithm>
#include <map>
using namespace std;
bool cmp(int a, int b) {
return a > b;
}
struct Node {
int Weight;
int Idx;
};
bool cmpw(struct Node a, struct Node b) {
return a.Weight > b.Weight;
}
int main() {
int Num;
scanf("%d", &Num);
int Hat[Num];
struct Node W[Num];
int tmpH[Num];
stack<int> H;
for (int i = 0; i < Num; i++) {
scanf("%d", &Hat[i]);
tmpH[i] = Hat[i];
// H.push(tmpH);
}
// printf("%d\n", H.size());
sort(Hat, Hat + Num, cmp);
map<int, int> HatMap;
for (int i = 0; i < Num; i++) {
HatMap[Hat[i]] = i;
}
for (int i = 0; i < Num; i++) {
scanf("%d", &W[i].Weight);
W[i].Idx = i + 1;
}
sort(W, W + Num, cmpw);
int check = 1;
for (int i = Num - 1; i >= 0; i--) {
// int tmp = H.top();
// H.pop();
// printf("%d ", tmpH[i]);
if (check) {
printf("%d", W[HatMap[tmpH[i]]].Idx);
check = 0;
} else {
printf(" %d", W[HatMap[tmpH[i]]].Idx);
}
}
return 0;
}
题意:给一个N,然后第二行给N个帽子的Size(以压入堆栈的形式), 第三行给N个人的体重(序号从1到N);体重越大的人戴越大的帽子,然后让你输出拿帽子的顺序,就是把第二行的数据一个个弹出堆栈,然后匹配相应的体重,输出那个人的序号;N的数值大概是10的5次方还是6次方,我给忘了。
说实话这题我没想到什么巧妙的方法,当时的思路就是先读入帽子Size,存到堆栈和一个数组里,对数组排序以后用map映射到0、1、2、3这些序号上;体重的话把序号和体重绑在一起存进数组,然后按体重排序,最后弹出一个帽子Size –> 序号 –>体重数组下标,然后输出下标.序号。
这题写完只剩一个半小时了,因为中途出了点小问题,用stack容器存第二行数据不知道为什么只能存一半,排查了一会才发现,最后临时用一个数组代替stack。
2.3第三题
#include <cstdio>
#include <stack>
#include <algorithm>
#include <map>
using namespace std;
#define MaxVNum 110
#define Null -1
int Nv, Ne;
int G[MaxVNum][MaxVNum];
int Visit[MaxVNum] = { false};
int MaxSpotNum = 0;
void DFS(int V, int spotNum) {
Visit[V] = true;
if (spotNum > MaxSpotNum) {
MaxSpotNum = spotNum;
}
for (int i = 1; i <= Nv; i++) {
if (!Visit[i] && G[V][i] != Null) {
DFS(i, spotNum + 1);
}
}
}
void Init() {
for (int i = 0; i <= Nv; i++) {
Visit[i] = false;
}
}
int main() {
int v1, v2, V;
scanf("%d%d", &Nv, &Ne);
for (int i = 1; i <= Nv; i++) {
for (int j = 1; j <= Nv; j++) {
G[i][j] = Null;
}
}
for (int i = 1; i <= Ne; i++) {
scanf("%d%d", &v1, &v2);
G[v1][v2] = G[v2][v1] = 1;
}
for (int i = 1; i <= Nv; i++) {
int CurSpotNum = MaxSpotNum;
DFS(i, 1);
Init();
if (CurSpotNum != MaxSpotNum) {
V = i;
CurSpotNum = MaxSpotNum;
}
}
printf("%d %d", V, MaxSpotNum);
return 0;
}
非常简单的DFS,今年的图题应该算特别简单了,一个无向无权图的DFS,问从哪个顶点DFS能访问最多的顶点,输出该顶点以及访问最多的顶点数。
有两个条件,一个是优先从顶点编号最小的开始访问,另一个是同一个顶点不会访问两次,前者其实就是DFS的时候从1到N还是从N到1的问题,但是按照惯例大家都是从1到N遍历的,所以基本上相当于无条件,后者用一个Visit数组记录每个顶点是否被访问过就可以;最后每个顶点用一次DFS,记录最大访问数即可。
2.4第四题
//第四题
#include <cstdio>
#include <stack>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
typedef struct Node Nd;
struct Node {
int P;
int Val;
};
bool cmp(Nd a, Nd b) {
return a.P < b.P;
}
typedef struct TNode *BinTree;
struct TNode {
Nd data;
BinTree Left;
BinTree Right;
};
void Insert(BinTree &T, Nd X) {
if (!T) {
T = new TNode;
T->data = X;
T->Left = T->Right = NULL;
return;
}
if (T->data.Val > X.Val) {
Insert(T->Left, X);
} else if (T->data.Val < X.Val){
Insert(T->Right, X);
}
}
void LayerOrder(BinTree T) {
queue<BinTree> q;
q.push(T);
int check = 1;
while (!q.empty()) {
BinTree tmp = q.front();
q.pop();
if (check) {
printf("%d", tmp->data.Val);
check = 0;
} else {
printf(" %d", tmp->data.Val);
}
if (tmp->Left) q.push(tmp->Left);
if (tmp->Right) q.push(tmp->Right);
}
printf("\n");
q.push(T);
check = 1;
while (!q.empty()) {
BinTree tmp = q.front();
q.pop();
if (check) {
printf("%d", tmp->data.P);
check = 0;
} else {
printf(" %d", tmp->data.P);
}
if (tmp->Left) q.push(tmp->Left);
if (tmp->Right) q.push(tmp->Right);
}
}
int main() {
int N;
scanf("%d", &N);
Nd t[N];
for (int i = 0; i < N; i++) {
scanf("%d %d", &t[i].Val, &t[i].P);
}
sort(t, t+N, cmp);
BinTree T = new TNode;
T->Left = T->Right = NULL;
T->data = t[0];
for (int i = 1; i < N; i++) {
Insert(T, t[i]);
}
LayerOrder(T);
return 0;
}
说实话这题我一开始也没看懂,做到这一题的时候还剩一个小时,题目大概说的是有一种树,结点是一个结构体,包括值和优先级,给出N个结点,让你自己建树然后输出层序遍历结果,第一行输出值,第二行输出优先级。可见题目唯一难度在于如何建树。
题目里还用到了heapSort这种词,我一开始以为是堆排序,看了一下好像也没关系,因为题目里面示例建出来的树也不是一棵完全二叉树,就这样看了二十多分钟题目,也没有什么思路,出来看了一下这道题居然是通过率最高的,赶紧再回头看了下题目,试着往简单的方向去想,最后发现把数据按照优先级排好,然后按照二叉搜索树的插入方式就可以建出和示例一样的树。。。虽然不知道为什么,但是就这样去写了。
建树加层序遍历应该是最基本的东西了,写了不到十分钟写完了,层序遍历那里着急了直接复制粘贴遍历两次,一次输出值,一次输出优先级,结果一次就AC了,到最后也没明白这题啥意思,等以后大佬解答吧
3.总结和感想
21秋季的题应该算是比较简单了,第一次参加PAT,我感觉难度更多是在理解题意上面,理解题目以后这四道题都是一次AC了,都不用花时间测试和调试,结合自己的经验和经历给大家几个小建议吧
1、STL容器和algorithm头文件的一些算法(特别是sort)非常非常非常有用,不是说不会就写不出题目,但是会了能节省很多时间,能把精力集中在解决问题而不是编码上;
2、我个人建议是先看完浙大网课,同时把课后编程题做完(用C语言),再用算法笔记学习STL容器等应试的技巧,最后有时间再配合上机实战指南刷PAT(最后这一步我都没做),这些事情做完我相信在PAT甲级可以取得优良的成绩。
复习重点的话其实很多大佬总结过了,我这就做个小总结吧,当作参考
1、基本线性结构肯定要熟练,数组链表是实现各种数据结构的基础;
2、建树以及基本操作(插入删除等),尤其是二叉搜索树;时间紧张的话AVL和哈夫曼不看也行,但是堆的调整插入删除还是要理解的;
3、建图以及图的遍历,要结合树一起深刻理解DFS和BFS的思想及实现方法;最短路径Dilkstra配合DFS也要会,时间紧张的话最小生成树Prim之类的不看也行,其实写法和Dilkstra差不多。
4、理解映射的思想。