PTA甲级模拟第十一弹:1124-1131

1. 知识点总结

本次作业没有严格按照试卷题目编排,同时跳过了之前(久远时)做过的题目,这样安排……别问,问就是想要填满空隙的某种不知名强迫症🤣

本次作业涉及到的知识点有

  • map+六级
  • 二叉树
  • 优先队列
  • 深度优先搜索
题号难度知识点
1124🐸🐸数组+map,英语😱阅读
1125🐸数组,找规律
1127🐸🐸二叉树,但是只想给一个半🐸,模板,注意输出STL重排序~
1129🐸🐸优先队列+结构体排序(不算难题,但是优先队列忘记怎么处理结构体,实属不应该)
1130🐸二叉树的中序遍历和括号之间的爱恨情仇
1131🐸🐸🐸其实不应该有这个难度,看完题解😱知道是比较基础的DFS变式主要是提醒自己DFS练少了

2. 分题题解

2.1 第一题 1124 Raffle for Weibo Followers (20 分)

【题目链接】

难点在于……英文叭,没看懂他的意思,感觉不是很难的题目,但是卡在阅读理解上着实难受:

题目的意思,不规定有几个中奖的人,只需要对于输入的人,从初始位置s,每隔着n个算一次中奖,如果有人重复中,s++,跳过这个人

第一遍照着模糊的理解写,暴力12/20,跪了😥,因为以为n是限制,然后m个人是围成一圈😶,错误理解打咩!!!正解在下面

#include<bits/stdc++.h>
using namespace std;
int m,n,s;//m种可能,转的次数,开始地方 
vector<string>fid;
map<string,bool>winned; 
set<string>st;
int main(){
	scanf("%d%d%d",&m,&n,&s);
	fid.resize(m);
	for(int i=0;i<m;i++){
		cin>>fid[i];
		winned[fid[i]]=false;
		st.insert(fid[i]);
	}
	if(st.size()<n||s>m){
		printf("Keep going...");
	}else{
		vector<string>ans;
		int cnt=0;
		s--;//开始的位置 
		while(cnt!=n){
			s%=m;
			if(winned[fid[s]]==false){
				winned[fid[s]]=true;
				ans.push_back(fid[s]);
				cnt++;
				s+=n;
			}else{
				s++;
			}
		} 
		//输出答案 
		for(int i=0;i<ans.size();i++){
			printf("%s\n",ans[i].c_str());
		}
		
	}
	return 0;
} 

订正之后:

参考柳神代码,简洁度被瞬间秒杀

#include<bits/stdc++.h>
using namespace std;
int m,n,s;//m种可能,转的次数,开始地方 

int main(){
	scanf("%d%d%d",&m,&n,&s);
	string str;
	bool flag=false;//是否有人中过奖 
	map<string,bool>winned;
	for(int i=1;i<=m;i++){
		cin>>str;
		if(winned[str]==true){
			s++;
		}else if(i==s){
			winned[str]=true;
			cout<<str<<endl;
			flag=true;
			s=s+n;
		}
	}
	if(flag==false)printf("Keep going...");
	return 0;
} 

2.2 第二题 1125 Chain the Ropes (25 分)

【题目链接】

找规律题,稳住心态:

先将输入的绳子长度片段按照从小到大排序,sum=(sum+v[i])/2,一直到最后,这样,短的被反复折叠,但是长的安然无恙,可以保证最后结果正确(本来以为是深搜,结果草稿纸上推了一下发现找规律比较简单~)

#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>v;
int main(){
	scanf("%d",&n);
	v.resize(n);
	for(int i=0;i<n;i++){
		scanf("%d",&v[i]);
	}
	sort(v.begin(),v.end());
	int sum=v[0];
	for(int i=1;i<n;i++){
		sum=(sum+v[i])/2;
	}
	printf("%d",sum);	

	return 0;
}

2.3 第三题 1130 Infix Expression (25 分)

【题目传送门】

树的中序遍历,括号的输出需要注意,最外层的括号和叶子节点括号不需要输出,其余的在递归过程中输出括号

#include<bits/stdc++.h>
using namespace std;
struct Node{
	char data[15];
	int left;
	int right;
};
vector<Node>nodes;
int n;
bool isLeaf(int a){
	if(a==-1)return false;
	if(nodes[a].left==-1&&nodes[a].right==-1){
		return true;
	}
	return false;
}
bool isOk(int a){
	if(a==-1)return false;
	if(isLeaf(nodes[a].left)&&isLeaf(nodes[a].right)){
		return true;
	}
	return false;
}
bool flag=false;
void preTravel(int root,int cnt){
	if(root==-1){
		return;
	}
	//bool flag=isOk(root);
	if(cnt&&!isLeaf(root)){
		printf("(");
	}
	preTravel(nodes[root].left,cnt+1);
	printf("%s",nodes[root].data);
	preTravel(nodes[root].right,cnt+1);
	if(cnt&&!isLeaf(root)){
		printf(")");
	}
} 
const int maxn=100;
bool isc[maxn]={false};
int main(){
	scanf("%d",&n);
	nodes.resize(n+1);
	for(int i=1;i<=n;i++){
		scanf("%s %d %d",nodes[i].data,&nodes[i].left,&nodes[i].right);
		isc[nodes[i].left]=true;
		isc[nodes[i].right]=true;
	}
	//找到根节点 
	int root=-1;
	for(int i=1;i<=n;i++){
		if(!isc[i]){
			root=i;
			break;
		}
	}
	preTravel(root,0);
	return 0;
}

2.4 第四题 1129 Recommendation System (25 分)

【题目传送门】

直觉告诉我应该用优先队列,然鹅……忘记优先队列怎么用了,翻看了API……罪过罪过

优先队列+结构体

每次输出的时候还得将不同id存到vector中,输出后再存回到优先队列中

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct Node{
	int id;
	int t;
};
bool operator<(Node a,Node b){
	if(a.t!=b.t){
		return a.t<b.t;
	}else{
		return a.id>b.id;
	}
}
priority_queue<Node>ans;
const int maxn=50009;
int cnt[maxn]={0};
Node temp;
int id;
vector<Node>op;
set<int>st; 
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&id);
		cnt[id]++;
		if(i!=1){
			printf("%d:",id);
			int output=k<st.size()?k:st.size();
			//输出 
			for(int x=0;x<output;x++){
				temp=ans.top();
				ans.pop();
				printf(" %d",temp.id);
				if(temp.id!=id){
					op.push_back(temp);
				}
			}
			//存回去
			for(int x=0;x<op.size();x++){
				ans.push(op[x]);
			}
			op.clear();
			printf("\n");
		}
		temp.id=id;
		temp.t=cnt[id];
		ans.push(temp);
		st.insert(id);
	}
	return 0;
} 

2.5 第五题 1127 ZigZagging on a Tree (30 分)

【题目链接】

感觉PTA一般考二叉树变式比较多,但是Dij、并查集、优先队列还是得熟练掌握,不然真的白给😂,(貌似、也许、好像其他机试不会这么痴迷于树的~

  • 中序遍历+后序遍历**—>**层序遍历输出结果
  • 结构体存储层次遍历输出的结果,重新排序后按照要求输出
#include<bits/stdc++.h>
using namespace std;
int N;
vector<int>post;
vector<int>in;
struct Node{
	int val;
	Node *left;
	Node *right;
	int level;
};
struct Ans{
	int data;
	int level;
	int cnt;
};
vector<Ans>ans;
bool cmp(Ans a,Ans b){
	if(a.level!=b.level){
		return a.level<b.level;
	}else{
		if(a.level%2==1){
			//单数层,从右往左
			return a.cnt>b.cnt; 
		}else{
			return a.cnt<b.cnt;
		}
	}
}
Node *build(int inL,int inR,int postL,int postR){
	if(inL>inR){
		return NULL;
	}
	Node *root=new Node;
	root->val=post[postR];
	int k;
	for(k=inL;k<=inR;k++){
		if(in[k]==post[postR]){
			break;
		}
	}
	int left_num=k-inL;
	root->left=build(inL,k-1,postL,postL+left_num-1);
	root->right=build(k+1,inR,postL+left_num,postR-1);
	return root;
}
void levelTravel(Node *root){
	queue<Node*>q;
	root->level=1;
	q.push(root);
	bool flag=false;
	Node *temp;
	int cnt=0;
	while(!q.empty()){
		cnt++;
		Node *top=q.front();
		q.pop();
		if(top->left!=NULL){
			temp=top->left;
			temp->level=top->level+1;
			q.push(temp);
		}
		if(top->right!=NULL){
			temp=top->right;
			temp->level=top->level+1;
			q.push(temp);
		}
		//保存每一行的 
		Ans a;
		a.level=top->level;
		a.data=top->val;
		a.cnt=cnt;
		ans.push_back(a);
	}
	return ;
}
int main(){
	scanf("%d",&N);
	post.resize(N);
	in.resize(N);
	for(int i=0;i<N;i++){
		scanf("%d",&in[i]);
	}
	for(int i=0;i<N;i++){
		scanf("%d",&post[i]);
	}
	Node*root=build(0,N-1,0,N-1);
	levelTravel(root);
	sort(ans.begin(),ans.end(),cmp);
	for(int i=0;i<N;i++){
		if(i)printf(" ");
		printf("%d",ans[i].data);
	}

	return 0;
} 

2.6 第六题 1131 Subway Map (30 分)

【题目链接】

碎碎念:感觉考搜索不是很多,难度的话,只要读懂题目,一般不是很卡时间(😁)~

因为花了一个下午才勉强上19……喵呜呜呜,在面壁了,所以直接看了博客的答案,柳神给的思路还是深搜……(标准简介版),有大佬说DFS在数据严格下其实是不可以的,PTA测试数据点没有非常严格,所以一般的DFS可以过

订正思路和注意点贴在第一版下面~😥

第一版:19/30

DFS感觉手生呜呜呜

#include<bits/stdc++.h>
using namespace std;
# define INF 99999999
struct Node{
	int sid;
	int line;
};
int n,k,num,sid;
int sign;
int vis[10000]={0};
vector<Node>taken;
vector<Node>final;//最终答案 
vector<vector<int> >lines;//每条线路经过的站点名称
map<int,vector<int> >stop; //每个站点属于哪个路线 
//计算最少的站台
int st,ed; 
int ans;
int changes;
int getNextSid(int pos,int line){
	int len=lines[line].size();
	for(int i=0;i<len;i++){
		if(lines[line][i]==pos){
			if(i==len-1){
				return -1;
			}else{
				return  lines[line][i+1];
			}
		}
	}
	return -1;
} 
int getPreSid(int pos,int line){
	int len=lines[line].size();
	for(int i=1;i<len;i++){
		if(lines[line][i]==pos){
			return lines[line][i-1];
		}
	}
	return -1;
} 
void dfs(int pos,int line,int gone,int cnt){
	/*当前站台编号,线路,已经经过的站点个数*/
	//答案不唯一的话,输出转乘最小的 
	//printf("当前站点%04d, 当前路线:%d ,当前经过站点个数%d\n",pos,line,gone); 
	gone++;
	vis[pos]=sign;
	Node node;
	node.line=line;
	node.sid=pos;
	taken.push_back(node);
	if(gone>ans||(gone==ans&&cnt>changes)){
		return;
	}
	//结束,不需要继续探索 
	if((pos==ed&&ans>gone)||(pos==ed&&ans==gone&&cnt<changes)){
		ans=gone;
		final=taken; 
		return;
	}
	//探索 
	for(int i=0;i<stop[pos].size();i++){
		int tline=stop[pos][i];
		//下一站: 
		int nextpos=getNextSid(pos,tline);
		int nc=cnt;
		if(tline!=line)nc++;
		if(nextpos!=-1&&vis[nextpos]!=sign){
			dfs(nextpos,tline,gone,nc);
			//回溯 
			taken.pop_back();
			vis[nextpos]=sign-1;
		}
		//上一站:
		int prepos=getPreSid(pos,tline);
		if(prepos!=-1&&vis[prepos]!=sign){
			dfs(prepos,tline,gone,nc);
			//回溯 
			taken.pop_back();
			vis[prepos]=sign-1;
		}
	} 
	return;
}
int main(){
	scanf("%d",&n);
	lines.resize(n); 
	for(int i=0;i<n;i++){
		scanf("%d",&num);
		for(int j=0;j<num;j++){
			scanf("%d",&sid);
			lines[i].push_back(sid);
			stop[sid].push_back(i);
		}	
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
		sign=i;
		taken.clear();
		scanf("%d%d",&st,&ed);
		ans=INF;
		changes=INF;
		for(int j=0;j<stop[st].size();j++){
			dfs(st,stop[st][j],0,0);
		}
		//输出答案 
		printf("%d\n",ans-1);
		bool flag1=false,flag2=false;//起点、终点是否输出过 
		int s=final[0].sid,e=final[0].sid,myline=final[0].line;
		for(int x=0;x<final.size();x++){
			if(final[x].line==myline){
				e=final[x].sid;
			}else{
				//不一样
				printf("Take Line#%d from %04d to %04d.\n",myline+1,s,e); 
				s=final[x-1].sid,e=final[x].sid,myline=final[x].line;
			}
		}
		printf("Take Line#%d from %04d to %04d.\n",myline+1,s,e); 
		
	}
	return 0;
}

DFS版本:

忽略Line路线的影响,先当作图论中的寻找最短路径问题,深搜展开,最后判断的时候先比较路径长度,长度一致的情况下比较线路站次数。

在传统的dfs求最短路径上,加上了lines【i】【j】数组存储某路段属于哪一个路线Line(因为题目中明确说没有重合的路段,只有重合的站点,再次反映审题的重要性!!!!)

  • 图论(DFS最短路径)
  • STL存储答案
  • 最后路线长度相同时,transCount(temppath)函数另外计算转站次数~

第二版:AC

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000;
const int inf=99999999;
vector<vector<int> >graph(maxn);//存储图_站点之间的连接关系 
int lines[maxn][maxn];
bool vis[maxn]={false};
vector<int>path,temppath;
int n,k,num,sid; 
int st,ed,pre;
int minCnt,minTrans;
int transCount(vector<int>temppath){
	int len=temppath.size();
	if(len<=2)return 0;
	
	int sum=0;
	int pre=temppath[0],sid=temppath[1];
	int preLine=lines[pre][sid];
	for(int i=2;i<len;i++){
		pre=temppath[i-1];
		sid=temppath[i];
		if(lines[pre][sid]!=preLine){
			sum++;
			preLine=lines[pre][sid];
		}
	}
	return sum;
}
void dfs(int pos,int cnt) {
	//当前车站号码,走过的站数
	if(pos==ed&&((cnt<minCnt)||(cnt==minCnt&&transCount(temppath)<minTrans))){
		//符合条件
//		printf("pos==%d\n",pos);
//		printf("debug:%d %d    real:%d %d\n",temppath[0],temppath[temppath.size()-1],st,ed); 
		minCnt=cnt;
		minTrans=transCount(temppath);
		path=temppath;
		return;
	} 
	if(pos==ed||cnt>minCnt){
		return;
	}
	//深搜
	for(int i=0;i<graph[pos].size();i++){
		int newpos=graph[pos][i];
		if(vis[newpos]==false){
			vis[newpos]=true;
			temppath.push_back(newpos);
			dfs(newpos,cnt+1);
			//回溯
			vis[newpos]=false;
			temppath.pop_back();
		}
	} 
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d%d",&num,&pre);
		for(int j=1;j<num;j++){
			scanf("%d",&sid);
			graph[pre].push_back(sid);
			graph[sid].push_back(pre);
			lines[sid][pre]=i;
			lines[pre][sid]=i;
			pre=sid;
			
		}
	}
	scanf("%d",&k);
	for(int i=0;i<k;i++){
		scanf("%d%d",&st,&ed);
		minCnt=inf;
		minTrans=inf;
		temppath.clear();
		temppath.push_back(st);
		vis[st]=true;
		dfs(st,0);
		vis[st]=false;
		printf("%d\n",minCnt);
		//输出答案
		int s=st,len=path.size();
		int preLine=lines[s][path[1]];
		for(int j=1;j<len;j++){
			if(lines[path[j-1]][path[j]]!=preLine){
				printf("Take Line#%d from %04d to %04d.\n",preLine+1,s,path[j-1]); 
				s=path[j-1];
				preLine=lines[path[j-1]][path[j]];
			}
		}
		//最后输出
		printf("Take Line#%d from %04d to %04d.\n",lines[path[len-2]][ed]+1,s,ed); 
//		for(int j=1;j<len;j++){
//			printf("line#%d: %04d->%04d\n",lines[path[j-1]][path[j]],path[j-1],path[j]);
//		}
		
	}
	
	return 0;
} 

挖个坑,我觉得Dij也是可以的~

3. 参考链接

  1. 1124题解——柳神
  2. C++优先队列在结构体中使用
  3. 1131题解——柳神(DFS)
  4. 1131题解——效率
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值