2020年蓝桥杯模拟赛—个人小结

2020年蓝桥杯模拟赛解题报告(CPP版本)
Dev-C++ 常用快捷键大全

第二题:约数个数

题目
【问题描述】
1200000有多少个约数(只计算正约数)。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

method1:求数 n n n的约数个数:

#include<bits/stdc++.h> 
using namespace std;
int main()
{
    int n;
    scanf("%d", &n);
    int r = (int)sqrt(1.0 * n);
    int sum = 0;
    if(r * r == n)
    {
        sum++;
        r--;
    }
    for(int i = 1; i <= r; i++)
    if(n % i == 0)
    {
        sum += 2;
    }
    printf("%d\n", sum);
    return 0;
}

method2:枚举

#include <iostream>

using namespace std;
const int N = 1200000;

int main() {
    int ans = 0;
    for (int i = 1; i <= N; ++i) {
        if (N % i == 0)
            ans++;
    }
    cout << ans << endl;
    return 0;
}

第三题 叶节点数

题目
【问题描述】
一棵包含有2019个结点的二叉树,最多包含多少个叶结点?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

思路
n=n0+n1+n2,为使叶子节点数(n0)最多,必须n1最小,设为0,而n0=n2+1

得n2=(2019-1)/2=1009

所以n0=1010

答案
1010

小结:

二叉树的五大性质及证明

数字9

【问题描述】
在1至2019中,有多少个数的数位中包含数字9?
注意,有的数中的数位中包含多个9,这个数只算一次。例如,1999这个数包含数字9,在计算时只是算一个数。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

#include<bits/stdc++.h> 
using namespace std;
int main()
{
	int sum=0;
  	for(int i=1;i<=2019;i++)
  	{
  		int t=i;
  		while(t>=1)
		{
  		if(t%10==9){
  			sum++;
  			break;
		}	
		  t=t/10;
		  }
	  }
	  cout<<sum<<'\n'; 
    return 0;
}

数位递增的数

题目
【问题描述】
一个正整数如果任何一个数位不大于右边相邻的数位,则称为一个数位递增的数,例如1135是一个数位递增的数,而1024不是一个数位递增的数。
给定正整数 n,请问在整数 1 至 n 中有多少个数位递增的数?
【输入格式】
输入的第一行包含一个整数 n。
【输出格式】
输出一行包含一个整数,表示答案。
【样例输入】
30
【样例输出】
26
【评测用例规模与约定】
对于 40% 的评测用例,1 <= n <= 1000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,i,j;
	cin>>n;
	int sum=0;
	char s[7];
	for(i=1;i<=n;i++){
		sprintf(s,"%d",i);
		for(j=1;j<strlen(s);j++){
			if(s[j]<s[j-1]){
				break;
			}
		}
		if(j==strlen(s)){
				sum++;
			}	
	}
	cout<<sum<<'\n';
	return 0;
}

个人小结

sprintf用法

长草 (模板题)

题目
【问题描述】
小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1。
小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。
这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,这四小块空地都将变为有草的小块。
请告诉小明,k 个月后空地上哪些地方有草。
【输入格式】
输入的第一行包含两个整数 n, m。
接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字母之间没有空格。如果为小数点,表示为空地,如果字母为 g,表示种了草。
接下来包含一个整数 k。
【输出格式】
输出 n 行,每行包含 m 个字母,表示 k 个月后空地的状态。如果为小数点,表示为空地,如果字母为 g,表示长了草。
【样例输入】
4 5
.g…

…g…

2
【样例输出】
gggg.
gggg.
ggggg
.ggg.
【评测用例规模与约定】
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= k <= 1000。

Method2:队中存放已经有草的小块

#include<bits/stdc++.h>
using namespace std;
int dirx[4]={-1,1,0,0};
int diry[4]={0,0,-1,1};
struct  block{
	int x;
	int y;
	int State;// 0 已计算过  1 未计算 
};
int Map[1001][1001];
int main(){
	queue <block> q;//队中存放已经有草的地方   
	int n,m;
	int i,j;
	memset(Map,0,sizeof(Map));
	cin>>n>>m;
	char g; 
	for(i=1;i<=n;i++){ //输入 
		for(j=1;j<=m;j++){
			cin>>g;
			if(g=='g'){
					block t;
					t.x=i;
					t.y=j;
					t.State=1;
					Map[i][j]=1;
					q.push(t);
			}
		}
	}
	int k,len;
	cin>>k;
	while(k--){
		len=q.size();
		for(i=1;i<=len;i++){
			block t = q.front();//取队首 
			if(t.State==1){ 
			for(int u=0;u<=3;u++){
				if(t.x+dirx[u]>=1&&t.x+dirx[u]<=n&&t.y+diry[u]>=1&&t.y+diry[u]<=m){// 上下左右方向在范围内 
					if(Map[t.x+dirx[u]][t.y+diry[u]]==0){
						block New;				
						Map[t.x+dirx[u]][t.y+diry[u]]=1;
						New.x=t.x+dirx[u];
						New.y=t.y+diry[u];
						New.State=1;
						q.push(New); 
					} 
				}
			}
		}
		q.pop();//将队首出队  
		t.State=0;
		q.push(t);//原先的队首入队尾 
		}			
}
for(i=1;i<=n;i++){
	for(j=1;j<=m;j++){
		if(Map[i][j]==1){
			cout<<'g';
		}
		else{
			cout<<'.';
		}
	}
	cout<<endl;
}
	return 0;
}

Method2(荐):队中仅存放可继续扩展的有草小块

#include<bits/stdc++.h>
using namespace std;
int dirx[4]={-1,1,0,0};
int diry[4]={0,0,-1,1};
struct  block{
	int x;
	int y;
};
int Map[1001][1001];//Map记录已有草的位置 
int main(){
	queue <block> q;//队中仅存放可继续扩展的有草小块  
	int n,m;
	int i,j;
	memset(Map,0,sizeof(Map));
	cin>>n>>m;
	char g; 
	for(i=1;i<=n;i++){ //输入 
		for(j=1;j<=m;j++){
			cin>>g;
			if(g=='g'){
					block t;
					t.x=i;
					t.y=j;
					Map[i][j]=1;
					q.push(t);
			}
		}
	}
	int k,len;
	cin>>k;
	while(k--){
		len=q.size();
		for(i=1;i<=len;i++){
			block t = q.front();//取队首
			for(int u=0;u<=3;u++){
				if(t.x+dirx[u]>=1&&t.x+dirx[u]<=n&&t.y+diry[u]>=1&&t.y+diry[u]<=m){// 上下左右方向在范围内 
					if(Map[t.x+dirx[u]][t.y+diry[u]]==0){
						block New;				
						Map[t.x+dirx[u]][t.y+diry[u]]=1;
						New.x=t.x+dirx[u];
						New.y=t.y+diry[u];
						q.push(New); 
					} 
				}
			}
		q.pop();//将队首出队  队首所示有草小块四周已拓展完毕 
		}			
}
for(i=1;i<=n;i++){
	for(j=1;j<=m;j++){
		if(Map[i][j]==1){
			cout<<'g';
		}
		else{
			cout<<'.';
		}
	}
	cout<<endl;
}
	return 0;
}

小结

bfs,广度优先搜索,用队列暂存当前有草的所有位置,每过一个月,将先有当前队列中已有元素依次出栈,将新长出来的草块入栈,用矩阵Map记录草块的当前情况。
此法可保证每块草地至多被计算一次,时间复杂度为 O ( N ∗ M ) O(N*M) O(NM)

序列计数

小明想知道,满足以下条件的正整数序列的数量:

  1. 第一项为 n;
  2. 第二项不超过 n;
  3. 从第三项开始,每一项小于前两项的差的绝对值。
    请计算,对于给定的 n,有多少种满足条件的序列。
    【输入格式】
    输入一行包含一个整数 n。
    【输出格式】
    输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
    【样例输入】
    4
    【样例输出】
    7
    【样例说明】
    以下是满足条件的序列:
    4 1
    4 1 1
    4 1 2
    4 2
    4 2 1
    4 3
    4 4
    【评测用例规模与约定】
    对于 20% 的评测用例,1 <= n <= 5;
    对于 50% 的评测用例,1 <= n <= 10;
    对于 80% 的评测用例,1 <= n <= 100;
    对于所有评测用例,1 <= n <= 1000。

思路

由本题第三点的递归定义,可以得到递归公式:
f ( p r e , c u r ) = f ( c u r , 1 ) + f ( c u r , 2 ) + . . . + f ( c u r , a b s ( p r e − c u r ) + 1 = ∑ i = 1 ∣ p r e − c u r ∣ f ( c u r , i ) + 1 f(pre,cur) = f(cur,1) + f(cur,2) + ...+f(cur,abs(pre-cur)+1 =\sum_{i=1}^{|pre-cur|}f(cur,i) +1 f(pre,cur)=f(cur,1)+f(cur,2)+...+f(cur,abs(precur)+1=i=1precurf(cur,i)+1
(其中 p r e pre pre表示前一个数, c u r cur cur表示当前数。)
时间复杂度: O ( n 3 ) O(n^3) O(n3)

1.直接递归(暴力,毫无疑问会超时)

#include<bits/stdc++.h> 
using namespace std;
int sum=0;
void r(int Abs,int now){
		sum++;
		sum=sum%10000;
	for(int i=1;i<Abs;i++){
		if(abs(now-i)>=2){

			r(abs(now-i),i);
		}
		else{
			sum++;
			sum=sum%10000;
		} 
	}
}

int main()
{
	int n;
	cin>>n;
	for(int j=1;j<=n;j++)
	{
			r(n-j,j);
	}
	cout<<sum<<'\n';
    return 0;
}

Or

#include<bits/stdc++.h> 
using namespace std;
int sum=0;
void r(int Abs,int now){
		sum++;
		sum=sum%10000;
	for(int i=1;i<Abs;i++){
		if(abs(now-i)>=2){

			r(abs(now-i),i);
		}
		else{
			sum++;
			sum=sum%10000;
		} 
	}
}
int main()
{
	int n;
	cin>>n;
	for(int j=1;j<=n;j++)
	{
			r(n-j,j);
	}
	cout<<sum<<'\n';
    return 0;
}

2.优化,将已计算结果暂存

  空间换时间,还是会超时,但已可通过大部分样例。
当 n= 1000时,已可在有限时间内计算出结果。

#include<bits/stdc++.h> 
using namespace std;
int sum=0;
int mem[1010][1010];

int r(int pre,int now){
		int num = 1;
		if(mem[pre][now]!=0){
			return mem[pre][now];
		}
	for(int i=1;i<abs(pre-now);i++){
			num += r(now,i)%10000;
		}
	mem[pre][now] = num;
	return num;
}

int main()
{
	int n;
	memset(mem,0,sizeof(mem));
	cin>>n;
	for(int j=1;j<=n;j++)
	{
			sum = (sum+r(n,j))%10000;
	}
	cout<<sum<<'\n';
    return 0;
}

3.进一步优化,减少循环次数(AC)

在上一种情况下, 解的空间是 n 2 n^2 n2,但因为展开循环累加,实际的复杂度还是 n 3 n^3 n3。若可以规避循环累加,则可以将复杂度优化到 n 2 n^2 n2
重新考虑状态转移,用 f ( i , j ) f(i,j) f(i,j)表示 p r e = i pre =i pre=i,当前数是 1 ∼ j 1\sim j 1j时候的合法序列个数。有:
f ( i , j ) = f ( i , j − 1 ) + f ( j , a b s ( i − j ) − 1 ) + 1 f(i,j)=f(i,j-1)+f(j,abs(i-j)-1)+1 f(i,j)=f(i,j1)+f(j,abs(ij)1)+1
这样每次解答树只需要展开两个节点,相当于少一次循环,尽管解答树的层次还是很深,但是有记忆存储的辅助,解空间依然是 n 2 n^2 n2。可以在更短的时间内解决问题。

#include<bits/stdc++.h> 
#define mod 10000
using namespace std;
int sum=0;
int mem[1010][1010];

int r(int now,int sub){
		if(sub<=0){
			return 0;
		}
		int num = 0;
		if(mem[now][sub]!=0){
			return mem[now][sub];
		}
	int res = r(now,sub-1)%mod+r(sub,abs(now-sub)-1)%mod+1;
	mem[now][sub] = res;
	return res;
}

int main()
{
	int n;
	memset(mem,0,sizeof(mem));
	cin>>n;
	cout<<r(n,n)<<'\n';
    return 0;
}

经验

如何判断自己的算法是否超时??
代码敲完后,如果可以,最大样例输入是一样,看能不能在有限的时间内出结果。一般不超时的算法,几秒钟内就可以出结果。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiu_cs

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值