PTA甲级模拟第三弹:1140-1143

感觉就是在赎大学三年没好好练习算法的罪孽

1 知识点总结

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

  • 字符串处理
  • STL+排序
  • 图论
  • BST树(时间复杂度)
题号 难度 知识点
1140 字符串和数字的转化+英语阅读理解
1141 STL+排序(涉及基础的字符串处理)
1142 图论(图基础的基础的基础,没啥知识点几乎)
1143 BST树的搜索与建立+时间复杂度优化最怕有思路但是卡时间的题目了(这题如果没有背过模板,考场上找规律应该更容易过一点)

2 分题题解

2.1 第一题

1140 Look-and-say Sequence (20 分)

思路

  • 找规律:出现的数字+该数字连续出现的次数……依次类推
  • 字符串处理:string与int之间的相互转化
#include
using namespace std;
int d,n;
//1 2 3 4 5 6 7 8
//D D1 D111 D113 D11231
bool vis[10];
int Count(string str, char ch){
	int ans=0;
	for(int i=0;i<str.length();i++){
		if(str[i]==ch){
			ans++;
		}
	}
	return ans;
}
string getAns(){
	//计算出d的第n个
	string nth=to_string(d);
	string n1th="";
	for(int i=1;i<n;i++){
		//初始化
		//for(int k=0;k<10;k++)vis[k]=false;
		n1th="";
		for(int j=0;j<nth.size();j++){
			int temp=1;
			n1th+=nth[j];
			while(j+1<nth.size()&&nth[j]==nth[j+1]){
				j++;
				temp++;
			}
			n1th+=to_string(temp);
		}
		nth=n1th;
		//cout<< nth<
	} 
	return nth;
}
int main(){
	scanf("%d%d",&d,&n);
	cout<<getAns();
	return 0;
} 

2.2 第二题

1141 PAT Ranking of Institutions (25 分)

思路

基本的字符串处理+排序,没有坑

学习到的一个新的string用法:

因为tolower()只支持一个字符一个字符修改,下面可以快速转化一个string(其实自己写转化函数也不费劲)

transform(iid.begin(),iid.end(),iid.begin(),::tolower);

#include
using namespace std;
struct Institution{
	string name;
	int TWS=0;
	int Ns=0;
	int scoreA=0;
	int scoreB=0;
	int scoreT=0;
};
map<string,Institution>mp;
int n;
string id;
int score;
string iid;
vector<Institution>v;
bool cmp(Institution a,Institution b){
	if(a.TWS!=b.TWS){
		return a.TWS>b.TWS;
	}else if(a.Ns!=b.Ns){
		return a.Ns<b.Ns;
	}else{
		return a.name<b.name;
	}
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		cin>>id>>score>>iid;
		//注意写法 
		transform(iid.begin(),iid.end(),iid.begin(),::tolower);
		if(id[0]=='A'){
			mp[iid].scoreA+=score;
		}else if(id[0]=='B'){
			mp[iid].scoreB+=score;
		}else if(id[0]=='T'){
			mp[iid].scoreT+=score;
		}
		mp[iid].Ns++;
		mp[iid].name=iid;
	}
	printf("%d\n",mp.size());
	for(map<string,Institution>::iterator it=mp.begin();it!=mp.end();it++){
		Institution temp=it->second;
		//temp
		temp.TWS=temp.scoreA+temp.scoreB/1.5+temp.scoreT*1.5; 
		v.push_back(temp);
	}
	sort(v.begin(),v.end(),cmp);
	int rank=1;
	for(int i=0;i<v.size();i++){
		if(i&&v[i].TWS!=v[i-1].TWS){
			rank=i+1;
		}
		cout<<rank<<" "<<v[i].name<<" "<<v[i].TWS<<" "<<v[i].Ns<<endl;
	}
	return 0;
} 

2.3 第三题

1142 Maximal Clique (25 分)

思路

基础图论

  • 首先判断给定点集是否两两相连(并在此时给点集中的点标记)
  • 其次判断是否有一个点不属于点集但是和点集中的所有点相连
  • 因为没怎么卡时间,所以暴力也基本可以
#include
using namespace std;
int n,m,k;
int num;
const int maxn=206;
int a,b;
bool graph[maxn][maxn]={false};
int v[maxn];
int vis[maxn]={0};
bool isADJ(int num){
	for(int i=0;i<num;i++){
		vis[i]=k;
		for(int j=i+1;j<num;j++){
			if(!graph[v[i]][v[j]]){
				return false;
			}
		}
	}
	return true;
}
bool canADD(int num,int sign){
	bool flag=false;
	for(int id=1;id<=n;id++){
		if(vis[id]!=sign){
			for(int i=0;i<num;i++){
				if(graph[v[i]][id]==false){
					break;
				}
				if(i==num-1&&graph[v[i]][id]==true){
					return true;
				}
			}
			
		} 
	}
	return false;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d",&a,&b);
		graph[a][b]=true;
		graph[b][a]=true;
	}
	scanf("%d",&k);
	for(int i=0;i<k;i++){
		scanf("%d",&num);
		for(int j=0;j<num;j++){
			scanf("%d",&v[j]);
		}
		//任意两个顶点都是有邻接的边的 
		if(!isADJ(num)){
			printf("Not a Clique\n");
		}else if(canADD(num,k)){
			printf("Not Maximal\n");
		}else{
			printf("Yes\n");
		}
		//没有办法再加任何一个邻接边 
	}
	

	return 0;
} 

2.4 第四题

[1143 Lowest Common Ancestor (30 分)](题目详情 - 1143 Lowest Common Ancestor (30 分) (pintia.cn))

狠狠订正嗷嗷嗷嗷

一开始的思路:

  • 建立一棵树(模板,建议熟悉)
  • 对于给定两个结点,首先Find看是否存在,并在寻找的过程中保存父节点(即经过的结点)
  • 比较沿路经过的父节点,是否有出现在同一层同一大小的结点,若有,即为公共父节点(具体情况还需要特判,若两个结点相等并且等于父节点,属于:X is an ancestor of Y情况)

第一版:18/30

#include
using namespace std;
//最低的祖先
int m,n; 
int v;
struct Node{
	int val;
	Node *right=NULL;
	Node *left=NULL;
};
Node *build(int val,Node*root){
	if(root==NULL){
		root=new Node;
		root->val=val;
	}else if(val<root->val){
		root->left=build(val,root->left);
	}else{
		root->right=build(val,root->right);
	}
	return root;
}
//找到最年轻的父节点
int a,b; 
vector<int>v1;
vector<int>v2;
bool Find(int val,Node*root,int sign){
	if(root==NULL){
		return false;
	}else if(root->val==val){
		if(sign==1){
			v1.push_back(root->val);
		}else{
			v2.push_back(root->val);
		}
		return true;
	}else{
		//保存路径上的值 
		if(sign==1){
			v1.push_back(root->val);
		}else{
			v2.push_back(root->val);
		}
		if(val<root->val){
			return Find(val,root->left,sign);
		}else{
			return Find(val,root->right,sign);
		}
	}
}
map<int,bool>mp;
int main(){
	scanf("%d%d",&m,&n);
	Node *root=NULL;
	for(int i=0;i<n;i++){
		scanf("%d",&v);
		mp[v]=true;
		root=build(v,root);
	}
	for(int i=0;i<m;i++){
		v1.clear();
		v2.clear();
		scanf("%d%d",&a,&b);
		if(!mp[a]||!Find(a,root,1)){
			if(!mp[b]||!Find(b,root,2)){
				printf("ERROR: %d and %d are not found.\n",a,b);
			}else{
				printf("ERROR: %d is not found.\n",a);
			}
		}else if(!mp[b]||!Find(b,root,2)){
			printf("ERROR: %d is not found.\n",b);
		}else{
			
			//找到路径上公共点
			int ans=-1;
			for(int i=0;i<min(v1.size(),v2.size());i++){
				if(v1[i]==v2[i]){
					ans=v1[i];
				}
			} 
			if(ans==a&&ans!=b){
				printf("%d is an ancestor of %d.\n",ans,b);
			}else if(ans==b&&ans!=a){
				printf("%d is an ancestor of %d.\n",ans,a);
			}else if(ans==a&&ans==b){
				printf("%d is an ancestor of %d.\n",ans,a);
			}else{
				printf("LCA of %d and %d is %d.\n",a,b,ans);
			}
		}
	}
	
	return 0;
}

第二版:试着将Find优化在build部分就储存父节点,还是不行18/30

#include
using namespace std;
//最低的祖先
int m,n; 
int v;
struct Node{
	int val;
	Node *right=NULL;
	Node *left=NULL;
};
map<int,bool>mp;
map<int,vector<int> >path;
Node *build(int val,Node*root){
	if(root==NULL){
		root=new Node;
		root->val=val;
		path[val].push_back(val);
	}else if(val<root->val){
		path[val].push_back(root->val);
		root->left=build(val,root->left);
	}else{
		path[val].push_back(root->val);
		root->right=build(val,root->right);
	}
	return root;
}
//找到最年轻的父节点
int a,b; 
int main(){
	scanf("%d%d",&m,&n);
	Node *root=NULL;
	for(int i=0;i<n;i++){
		scanf("%d",&v);
		mp[v]=true;
		root=build(v,root);
	}
	for(int i=0;i<m;i++){
		
		scanf("%d%d",&a,&b);
		if(!mp[a]){
			if(!mp[b]){
				printf("ERROR: %d and %d are not found.\n",a,b);
			}else{
				printf("ERROR: %d is not found.\n",a);
			}
		}else if(!mp[b]){
			printf("ERROR: %d is not found.\n",b);
		}else{
			//找到路径上公共点
			int ans=-1;
			for(int i=min(path[a].size(),path[b].size())-1;i>=0;i--){
				if(path[a][i]==path[b][i]){
					ans=path[a][i];
					break;
				}
			} 
			if(ans==a&&ans!=b){
				printf("%d is an ancestor of %d.\n",ans,b);
			}else if(ans==b&&ans!=a){
				printf("%d is an ancestor of %d.\n",ans,a);
			}else if(ans==a&&ans==b){
				printf("%d is an ancestor of %d.\n",ans,a);
			}else{
				printf("LCA of %d and %d is %d.\n",a,b,ans);
			}
		}
	}
	
	return 0;
}

看了柳神的思路才知道不需要建立树,考察的是对于BST树数组结构

订正思路

分析:map mp用来标记树中所有出现过的结点,遍历一遍pre数组,将当前结点标记为a,如果u和v分别在a的左、右,或者u、v其中一个就是当前a,说明找到了这个共同最低祖先a,退出当前循环~最后根据要求输出结果即可~

#include
using namespace std;
int m,n;
vector<int>pre;
map<int,bool>mp;
int u,v;
int main(){
	scanf("%d%d",&m,&n);
	pre.resize(n);
	for(int i=0;i<n;i++){
		scanf("%d",&pre[i]);	
		mp[pre[i]]=true;	
	}
	for(int i=0;i<m;i++){
		scanf("%d%d",&v,&u);
		if(!mp[v]){
			if(!mp[u]){
				printf("ERROR: %d and %d are not found.\n",v,u);
			}else{
				printf("ERROR: %d is not found.\n",v);
			}
		}else {
			if(!mp[u]){
				printf("ERROR: %d is not found.\n",u);
			}else{
				//找到了
				int ans=-2;
				int temp;
				for(int j=0;j<n;j++){
					temp=pre[j];
					if(temp<u&&temp>v||temp<v&&temp>u||temp==u||temp==v){
						ans=temp;
						break;
					}
				}
				if(temp!=v&&temp!=u){
					printf("LCA of %d and %d is %d.\n",v,u,ans);
				}else{
					printf("%d is an ancestor of %d.\n",ans,ans==v?u:v);
				}
			}
		}
	}
	
	
	
}

3 参考资料

  1. STL中tolower()对于string的使用
  2. 柳神——1143的数据结构解法

你可能感兴趣的:(算法笔记入门,图论,数据结构,c++)