PTA甲级模拟第七弹:1160-1163

1. 知识点总结

卡在了第一题……然后没有⏲

还好不是考试……不然心态可以说炸得没边了😱

但是其他的题目比较基础,Dijkstra需要好好复习,好久没做过了

题号难度知识点
1160🐸🐸数学+找规律+搜索DFS+剪枝(个人觉得需要三刷)
1161🐸链表
1162🐸二叉树遍历
1163🐸Dijkstra

2. 分题题解

2.1 第一题

1160 Forever(20 分)

比较搞心态的一题,一开始注意到了末尾的数字只可以是9,不然n=m+1是无法导出n与m公因数大于2的,但是第一遍代码完全没有涉及这个……因为以为不是很大的点……

第一次:2/20

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
int m;
int k;
//素数判断 
bool isPrime(ll x){
	if(x<=2)return false;
	if(x==3||x==5||x==7)return true;
	for(ll i=2;i*i<=x;i++){
		if(x%i==0)return false;
	}
	return true;
} 
//最大公因数:辗转相除法 a>b 
ll maxDiv(ll a,ll b){
	if(a<b){
		swap(a,b);
	}
	ll r=a;
	while(r!=0){
		r=a%b;
		a=b;
		b=r;
	} 
	return a;
	
}
struct Ans{
	int n;
	ll num;
};
//计算整数每位相加之和
int  CountDigits(string now){
	int len=now.size();
	int sum=0;
	for(int i=0;i<len;i++){
		sum+=now[i]-'0';
	}
	return sum;
}
//容许的最小值
int lowest=0;
void CountLowest(int digit_num,int sum,int& lowest){
	if(digit_num==1){
		lowest=max(lowest,sum);
	}
	else{
		digit_num--;
		int temp=sum-9*digit_num;
		lowest=max(lowest,temp);
	}
	//printf("%d个数字 ,和为%d,最小值%d\n",digit_num+1,sum,lowest);
} 
//深搜:搜索所有符合条件的解
vector<Ans>ans;
void dfs(int num,int sum,string now,int digit_num,int mylow){
	//printf("digit_num=%d now=%s\n",digit_num,now.c_str());
	//当前遍历到的数字是num,当前和为sum,now存储目前的所有数字 
	now=now+to_string(num);
	sum+=num;
	digit_num++;
	if(sum>m||digit_num>k)return;
	//知道接下来不够了
	if(sum+9*(k-digit_num)<m)return; 
	if(sum==m&&digit_num==k){
		//1 0  && 11 10不可能有大于2的公因数 
		ll candidate=stol(now);
		string candidate1=to_string(candidate+1);
		int dsum=CountDigits(candidate1);
		//printf("&&&&&&&&&&&&&&&&&&&dsum=%d m=%d\n",dsum,m);
		if(isPrime(maxDiv(dsum,m))){
			Ans temp;
			temp.n=dsum;
			temp.num=candidate;
			ans.push_back(temp);
		}
		return;
	} 
	//继续探索
	CountLowest(k-digit_num,m-sum,mylow);
	for (int i=lowest;i<10;i++){
		dfs(i,sum,now,digit_num,mylow);
	}
	return;
} 

int main(){
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		scanf("%d%d",&k,&m);
		//找到k个数字组成的整数A,它的和是m
		//n=(A+1)和
		//m和n的最大公因数为大于2的素数
		CountLowest(k,m,lowest);
		for(int i=lowest;i<10;i++){
			if(i==0)continue;
			dfs(i,0,"",0,0);
		}
		printf("Case %d\n",i);
		int len=ans.size();
		if(len==0){
			printf("No Solution\n");
		}
		for(int i=0;i<len;i++){
			printf("%d %lld\n",ans[i].n,ans[i].num);
		}
		ans.clear();
	}
	return 0;
} 

第二次:订正

第一版:10分

#include<bits/stdc++.h>
using namespace std;
//有i个9,m-n=8+9*(i-1)
int n;
int k,m;
typedef long long ll;
bool Prime(ll x){
	if(x<=2)return false;
	for(ll i=2;i*i<=x;i++){
		if(x%i==0)return false;
	}
	return true;
}
ll gcd(ll a,ll b){
	if(a<b)swap(a,b);
	ll r=b;
	while(r!=0){
		r=a%b;
		a=b;
		b=r;
	}
	return a;
}
struct Ans{
	ll num;
	int n;
}; 
vector<Ans>ans;
int target_sum;
int digit_sum;
string nine;
void dfs(int pos,int num,int sum,string now){
	//cout<<"now "<<now<<endl;
	pos++;
	sum+=num;
	now+=to_string(num);
	if(sum==target_sum&&pos==digit_sum){
		//ans.push_back(stol(now+nine));
		Ans temp;
		temp.n=n;
		temp.num=stol(now+nine);
		ans.push_back(temp);
		return;
	}
	if(sum>target_sum||pos>digit_sum){
		return;
	}
	for(int i=0;i<=min(9,target_sum-sum);i++){
		dfs(pos,i,sum,now);
	}
	return ;
}
int N;
bool cmp(Ans a,Ans b){
	if(a.n!=b.n){
		return a.n<b.n;
	}else{
		return a.num<b.num;
	}
}
int main(){
	scanf("%d",&N);
	for(int l=1;l<=N;l++){
		printf("Case %d\n",l);
		scanf("%d%d",&k,&m);
		bool flag=false;
		for(int num9=1;num9<=k;num9++){
			//末尾num9的个数
			n=m-(8+9*(num9-1));
			if(n<=1){
				break;
			}
			int div=gcd(m,n);
			if(Prime(div)){
				//合法 
				target_sum=m-num9*9;
				digit_sum=k-num9;
				nine="";
				for(int c=0;c<num9;c++){
					nine+="9";
				}
				for(int i=1;i<=min(9,target_sum);i++){
					dfs(0,i,0,"");
				}
			} 
			
		}
		//输出答案
		int len=ans.size();
		if(len!=0){
			flag=true;
		}
		sort(ans.begin(),ans.end(),cmp);
		for(int c=0;c<len;c++){
			printf("%d %lld\n",ans[c].n,ans[c].num);
		} 
			ans.clear();
		if(!flag){
			printf("No Solution\n");
		}
	}
	return 0;
}

第二版:AC

#include<bits/stdc++.h>
using namespace std;
struct Ans{
	int n;
	int A;
};
int N;
int k,m;
int n;
bool isPrime(int x){
	if(x<=2)return false;
	if(x==2||x==3||x==5||x==7)return true;
	for(int i=2;i*i<=x;i++){
		if(x%i==0)return false;
	}
	return true;
}
int gcd(int a ,int b){
	//a>b 
	return b==0?a:gcd(b,a%b);
}
int digitSum(int x){
	int sum=0;
	while(x){
		sum+=(x%10);
		x/=10;
	}
	return sum;
}
vector<Ans>ans;
void dfs(int A,int sum,int rest_k){
	if(rest_k==0){
		if(sum==m){
			int n=digitSum(A+1);
			if(isPrime(gcd(m,n))){
				ans.push_back({n,A});
			}
		}
	}else if(rest_k>0){
		for(int i=0;i<=9;i++){
			//剪枝
			if(sum+i<=m&&sum+i+(rest_k-1)*9>=m){
				dfs(A*10+i,sum+i,rest_k-1);
			} 
		}
	}
}
bool cmp(Ans a,Ans b){
	if(a.n!=b.n){
		return a.n<b.n;
	}else{
		return a.A<b.A;
	}
}
int main(){
	scanf("%d",&N);
	for(int times=1;times<=N;times++){
		scanf("%d%d",&k,&m);
		printf("Case %d\n",times);
		//dfs搜索答案
		for(int i=1;i<=9;i++){
			dfs(i,i,k-1);
		} 
		sort(ans.begin(),ans.end(),cmp);
		//输出答案
		int len=ans.size();
		for(int i=0;i<len;i++){
			printf("%d %d\n",ans[i].n,ans[i].A);
		}
		ans.clear(); 
		if(len==0){
			printf("No Solution\n");
		}
	}
	return 0;
} 

2.2 第二题 Merging Linked Lists

链表的基础题(混合STL),注意输出格式以及-1的位置

#include<bits/stdc++.h>
using namespace std;
//链表操作
struct Node{
	int addr;
	int val;
	int next;
};
int head1;
int head2; 
int n;
int addr,val,next_addr;
map<int,Node>nodes;
vector<Node>v1;
vector<Node>v2;
void Change(vector<Node>v1,vector<Node>v2){
	int lena=v1.size();
	int lenb=v2.size();
	int j=lenb-1;
	for(int i=0;i<lena;i++){
		if(i!=lena-1)
			printf("%05d %d %05d\n",v1[i].addr,v1[i].val,v1[i+1].addr);
		else
			printf("%05d %d -1\n",v1[i].addr,v1[i].val);
		if(j>=0){
			i++;
			//打印 
			if(1)
				printf("%05d %d %05d\n",v1[i].addr,v1[i].val,v2[j].addr);
			else
				printf("%05d %d -1\n",v1[i].addr,v1[i].val);
			//打印
			if(i+1!=lena)
				printf("%05d %d %05d\n",v2[j].addr,v2[j].val,v1[i+1].addr);
			else
				printf("%05d %d -1\n",v2[j].addr,v2[j].val); 
			j--;
		}
	}
	
}
int main(){
	scanf("%d%d%d",&head1,&head2,&n);
	for(int i=0;i<n;i++){
		scanf("%d%d%d",&addr,&val,&next_addr);
		Node temp;
		temp.addr=addr;
		temp.val=val;
		temp.next=next_addr;
		nodes[addr]=temp;
	}
	int p=head1;
	//遍历v1
	while(p!=-1){
		Node temp=nodes[p];
		v1.push_back(temp);
		p=temp.next;
	} 
	//遍历v2
	p=head2;
	while(p!=-1){
		Node temp=nodes[p];
		v2.push_back(temp);
		p=temp.next;
	}  
	int len1=v1.size();
	int len2=v2.size();
	if(len1>=2*len2){
		Change(v1,v2);//大 小 
	}else {
		Change(v2,v1);	
	} 
	
	
	return 0;
}

2.3 第三题 Postfix Expression

这一题考察静态二叉树的遍历,需要注意的是:

  • 在正常的后续遍历中需要判断是否出现某一结点是“+”、“-”且没有左孩子的情况(也就是此时非运算符,而是符号表示),此时后序遍历改成前序遍历
  • root的查找主要是看输入的时候,哪一个结点没有作过孩子
#include<bits/stdc++.h>
using namespace std;
//静态二叉树
struct Node{
	char data[15];
	int left;
	int right;
};
int n;
vector<Node>v;
void postTravel(int root){
	if(root==-1){
		return;
	}
	
	if((v[root].data[0]=='-'||v[root].data[0]=='+')&&strlen(v[root].data)==1&&v[root].left==-1){
		printf("(");
		printf("%s",v[root].data);
		postTravel(v[root].left);
		postTravel(v[root].right);
		printf(")");
	}else{
		printf("(");
		postTravel(v[root].left);
		postTravel(v[root].right);
		printf("%s",v[root].data);
		printf(")");
	}
	
	
}
int root;
bool isNotRoot[50]={false};
int main(){
	scanf("%d",&n);
	v.resize(n+1);
	for(int i=1;i<=n;i++){
		scanf("%s%d%d",v[i].data,&v[i].left,&v[i].right);
		if(v[i].left!=-1){
			isNotRoot[v[i].left]=true;
		}
		if(v[i].right!=-1){
			isNotRoot[v[i].right]=true;
		}
	}
	for(int i=1;i<=n;i++){
		if(!isNotRoot[i]){
			root=i;
			break;
		}
	}
	postTravel(root);
	return 0;
}

2.4 第四题 Dijkstra Sequence

本想要不要深搜将每一个(st,ed)对应得所有可能性输出,和q中询问得序列比较,不过后来直接在Dij函数中将u的初始化引入q,在算法过程中直接比较,时间空间都可以节省😜

#include<bits/stdc++.h>
using namespace std;
const int INF=99999999;
//检查一个序列是否是dijkstra序列
int n,m,k; 
const int maxn=1009;
int graph[maxn][maxn];
int u,v,d;
vector<int>q;
vector<int>temp;
bool Dij(int st,int ed){
	//st到ed的最短距离 
	temp.clear();
	vector<int>dis(n+1);//起点到各个顶点的最短距离
	vector<bool>vis(n+1);
	fill(vis.begin(),vis.end(),false);
	fill(dis.begin(),dis.end(),INF);
	dis[st]=0;//起点自身的距离为0
	int id=0;
	for(int i=1;i<=n;i++){
		//int u=-1,min_dis=INF;
		int u=q[id];
		int min_dis=dis[u];
		id++;
		for(int j=0;j<=n;j++){
			if(vis[j]==false&&dis[j]<min_dis){
				return false;
			}
		}
		if(u==-1){
			break;
		}
		temp.push_back(u);
		vis[u]=true;
		for(int v=1;v<=n;v++){
			if(!vis[v]&&graph[u][v]!=-1&&dis[v]>graph[u][v]+dis[u]){
				dis[v]=graph[u][v]+dis[u];
			}
		}
	} 
	return true;
}
int main(){
	scanf("%d%d",&n,&m);
	memset(graph,-1,sizeof(graph));
	
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&u,&v,&d);
		graph[u][v]=d;
		graph[v][u]=d;
	}
	scanf("%d",&k);
	q.resize(n);
	for(int i=0;i<k;i++){
		//询问序列 
		for(int j=0;j<n;j++){
			scanf("%d",&q[j]);
		}
		//处理
		int st=q[0];
		int ed=q[n-1]; 
		if(Dij(st,ed)){
			printf("Yes\n");
		}else{
			printf("No\n");
		}
	}
	return 0;
}

3. 参考链接

  1. 1160博客思路讲解
  2. 1160博客题解(比较简单的一版DFS)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值