PAT1148-1151解题报告及考试日志

昨天拿到了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);
	}
}

 

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_38658814/article/details/82560477