昨天拿到了PAT甲级的满分,于是借机写一波解题报告和和考试历程,对应现在甲级习题集的1148-1151题。
先晒一下一次AC的提交记录~确实是运气太好,全部一次过,这是之前模拟的时候从未达成的。
说说考试的心路历程:第一题确实比较坑,很多人都被卡住了,我也不例外。刚开始没进入状态加上英语不好,阅读理解了五分钟还是似懂非懂。然后随手点了一下提交记录,发现第一题已经有十多个提交,但没人通过,而第二题居然直接有大佬AC了!当场我就惊呆了。。。估计大佬经历了读第一题——选择跳过——读第二题——秒杀,而这一切只花了不到五分钟orz。。。当时心想既然大佬都判断第一题不好做,那我是不是也应该跳过,但周围噼里啪啦的键盘声又让我觉得大家似乎都有了思路(现在想来估计很多人是在敲头文件吧。。。),最终决定再想五分钟,做不出就跳。然而思路偏了,一直在画方格图,试图理清推理方式或找到递推公式,五六分钟后仍没头绪,于是果断先放弃。好在2、3、4题都在刷题库的时候做过类似的,没遇到什么困难,顺利完成。其中2、3题算简单题,第4题难一些但是和题库1143太像。我比较幸运,考前挑了些30分的题2刷,其中就有1143,当时思考了一下后发现可以利用map把第四题转换成和1143一样的做法,于是最难的题也顺利搞定了(不过其中建树的函数有bug,找了10分钟吧,Dev-C++啥都好,就是我不会调试)。接着回头怼第一题,因为做完第3、4题的时候看了下排行,分别是并列第一和第二,估计大家都是把第一题跳了,于是我也默认第一题很难,拿出第二张草稿纸继续画图推导。当时真是走进死胡同了,总推不出来,心想这么难的题谁会做啊,于是退回又看了一眼通过人数——已经有60多人过了。然后突然就顿悟了:我是在用计算机啊,暴力枚举让电脑帮我算不就好了,O(100*100*100)=O(1000*1000),所以枚举估计也就3、4毫秒的事。最后终于把第一题解决,排名第五,走出考场的时候外面空空荡荡,就感觉很舒服~
接下来是解题报告,考虑到保持考场上的思考痕迹(其实就是懒),就直接把当时写的代码复制过来了,代码并不简练,可以说是想的少写的多,但这种策略是适合在考场上使用的。还有,为了节约时间,我一开就把所有头文件都写上然后复制粘贴建好cpp,所以头文件很冗杂,为方便阅读以下程序我把头文件都删了。
1148 Werewolf - Simple Version
题目叙述强行复杂化了,其实就是N个玩家里有2个狼人,其他为村民,而狼人和村民中都恰好各有一个人说谎。然后根据他们的叙述,找出两匹狼。
这道题其实不难,因为固定了恰有两匹狼,所以直接枚举两匹狼的所有可能编号,判断每种情况是否满足恰好1狼1人说谎的条件即可。
int N;
int mystate[110];
bool judge(int a,int b);
int main(){
scanf("%d",&N) ;
int i,x,j;
for(i=1;i<=N;++i){
scanf("%d",&mystate[i]);
}
for(i=1;i<=N;++i){
for(j=i+1;j<=N;++j){
if(judge(i,j)){
printf("%d %d\n",i,j);
return 0;
}
}
}
printf("No Solution\n");
return 0;
}
bool judge(int a,int b){
int cntwol=0,cntpeo=0;
int i,x;
for(i=1;i<=N;++i){
if(cntwol>1 || cntpeo>1){
return false;
}
x=mystate[i];
if(x>0){//人
if(x==a || x==b){
if(i==a || i==b){
++cntwol;
}
else{
++cntpeo;
}
// printf("i=%d\n",i);
}
}
else{//狼
x=-x;
if(x!=a && x!=b){
if(i==a || i==b){
++cntwol;
}
else{
++cntpeo;
}
// printf("i=%d\n",i);
}
}
}
if(cntwol==1 && cntpeo==1){
return true;
}
else{
return false;
}
}
1149 Dangerous Goods Packaging
题目先给了一些不能放一起的商品组合,然后给出几组商品,判断它们能不能放一起。
属于简单题,记得和题库里一道单身狗的题目类似,不过我没找到题号。总之,本题直接开一个10^6的vector<int>数组,保存不能放一起的商品的信息即可。
int N,M,K;
vector<int> mypair[1000010];
bool flag[1000010];
int main(){
scanf("%d%d",&N,&M);
int i,a,b,j;
for(i=0;i<N;++i){
scanf("%d%d",&a,&b);
mypair[a].push_back(b);
mypair[b].push_back(a);
}
int x;
bool isOK;
for(i=0;i<M;++i){
memset(flag,0,sizeof(flag));
scanf("%d",&K);
isOK=true;
for(j=0;j<K;++j){
scanf("%d",&x);
if(!isOK){
continue;
}
flag[x]=true;
for(int k=0;k<mypair[x].size();++k){
int tmp=mypair[x][k];
if(flag[tmp]==true){
isOK=false;
break;
}
}
}
if(isOK){
printf("Yes\n");
}
else{
printf("No\n");
}
}
return 0;
}
1150 Travelling Salesman Problem
也是一道和题库里某题类似的题,并且不需要什么数据结构知识,按照给出的TS cycle定义做即可。思考量不大,但是一定要细致。
const int inf=0x3f3f3f3f;
int N,M,K;
int G[210][210];
bool visit[210];
int len;
bool flagTs,flagsimple;
int minlen=inf,imin=-1;
bool isvisitall(){
for(int i=1;i<=N;++i){
if(visit[i]==false){
return false;
}
}
return true;
}
int main(){
int a,b,l;
scanf("%d%d",&N,&M);
int i;
memset(G,inf,sizeof(G));
for(i=0;i<M;++i){
scanf("%d%d%d",&a,&b,&l);
G[a][b]=G[b][a]=l;
}
scanf("%d",&K);
int n,j,start,end;
for(i=1;i<=K;++i){
memset(visit,0,sizeof(visit));
len=0;
flagTs=true;
flagsimple=true;
scanf("%d",&n);
if(n<=N){
flagTs=false;
}
if(n!=N+1){
flagsimple=false;
}
scanf("%d",&a);
start=a;
visit[a]=true;
for(j=1;j<n;++j){
scanf("%d",&b);
if(len<0){
continue;
}
visit[b]=true;
if(G[a][b]<inf){
len+=G[a][b];
}
else{
len=-1;
}
a=b;
}
end=b;
printf("Path %d: ",i);
if(len<0){
printf("NA (Not a TS cycle)\n");
continue;
}
if(end!=start){
printf("%d (Not a TS cycle)\n",len);
continue;
}
if(isvisitall()==false){
printf("%d (Not a TS cycle)\n",len);
continue;
}
if(minlen > len){
minlen=len;
imin=i;
}
if(flagsimple){
printf("%d (TS simple cycle)\n",len);
}
else{
printf("%d (TS cycle)\n",len);
}
}
printf("Shortest Dist(%d) = %d\n",imin,minlen);
return 0;
}
1151 LCA in a Binary Tree
先给出一棵树的中序遍历和前序遍历,然后给出多组查询数据,求每组数据的最低公共祖先节点。
本题和1143很像,做过那道题就好说了。利用unordered_map,创建变量posin,用于保存某个数在中序遍历中的位置。这样的话,如果知道节点A是节点U的祖先节点,那么,若posin[U]<posin[A]则说明U在A的左子树上,反之,U在A的右子树上。于是,我们把此题转换成了和1143一样的题目。而且,因为有了posin,建树也会方便许多~不得不说STL真是太强大了!
PS:根据遍历情况构建二叉树考过很多次了,一定要熟练掌握!
const int MAX=10010;
typedef struct node* Tree;
struct node{
Tree left,right;
int data;
node(int d){
data=d;
left=right=nullptr;
}
};
int M,N,K;
int in[MAX];
int pre[MAX];
unordered_map<int,int> posin;
int U,V,A,posU,posV;
int findU,findV;
Tree CreatTree(int preL,int preR,int inL,int inR);
void outputnotfind();
void findA(Tree root);
int main(){
scanf("%d%d",&M,&N);
int i;
for(i=0;i<N;++i){
scanf("%d",&in[i]);
posin[in[i]]=i;
}
for(i=0;i<N;++i){
scanf("%d",&pre[i]);
}
Tree root=nullptr;
root=CreatTree(0,N-1,0,N-1);
// printf("%d",root->data);
for(i=0;i<M;++i){
scanf("%d%d",&U,&V);
findU=posin.count(U);
findV=posin.count(V);
if(findU==0 || findV==0){
outputnotfind();
continue;
}
posU=posin[U];
posV=posin[V];
findA(root);
if(A!=U && A!=V){
printf("LCA of %d and %d is %d.\n",U,V,A);
continue;
}
if(A==U){
printf("%d is an ancestor of %d.\n",U,V);
continue;
}
else{
printf("%d is an ancestor of %d.\n",V,U);
}
}
return 0;
}
Tree CreatTree(int preL,int preR,int inL,int inR){
if(preL>preR){
return nullptr;
}
if(preL==preR){
return new struct node(pre[preL]);
}
int mypos=posin[pre[preL]];
int leftlen=mypos-inL;
Tree T=new struct node(pre[preL]);
T->left=CreatTree(preL+1,preL+leftlen,inL,mypos-1);
T->right=CreatTree(preL+leftlen+1,preR,mypos+1,inR);
return T;
}
void outputnotfind(){
if(!findU && !findV){
printf("ERROR: %d and %d are not found.\n",U,V);
return;
}
if(findU==0){
printf("ERROR: %d is not found.\n",U);
}
else{
printf("ERROR: %d is not found.\n",V);
}
}
void findA(Tree root){
if(!root){
return ;
}
A=root->data;
int posroot=posin[A];
if(posU==posroot ||posV==posroot){
return ;
}
if(posU<posroot && posV>posroot){
return;
}
if(posU>posroot && posV<posroot){
return;
}
if(posU<posroot && posV<posroot){
findA(root->left);
}
else{
findA(root->right);
}
}