蓝桥杯模拟赛记录

1.

大意就是给一个IPv4地址,里面有一个数不知道,问该数最大是多少
答案:常识,显然255


2.

如果一个整数 g 能同时整除整数 A 和 B,则称 g 是 A 和 B 的公约数。例如:43 是 86 和 2021 的公约数。
请问在 1(含) 到 2021(含) 中,有多少个数与 2021 存在大于 1 的公约数。请注意 2021 和 2021 有大于 1 的公约数,因此在计算的时候要算一个。

只要计算出所有因数放到集合里,并把它们的倍数也放到集合里就可以了。
做的时候出了点低级的错误:把每个因数都设为now,结果加的时候每次都是+=now了,这就导致只有 n o w ∗ 2 i now*2^i now2i算进去了。导致算出错误结果13
正确的代码如下:

#include 
#include 
#include 
#include 
using namespace std;

int main() {
	set<int> fac;
	int n=2021;
	int x=(int)sqrt(n);
	fac.insert(n);
	for(int i=2; i<=x; i++) {
		if(n%i==0) {
			fac.insert(i);
			fac.insert(n/i);
			//printf("%d %d ",i,n/i);
		}
	}
	set<int>::iterator it=fac.begin(),it2=fac.end();
	for(; it!=it2; it++) {
		int now=*it;
		while(now<n) {
			fac.insert(now);
			now+=*it;
			//注意加的是原数!
		}
	}
//	it=fac.begin();
//	for(; it!=fac.end(); it++)	printf("%d ",*it);
	printf("%u",fac.size());
	return 0;
}

正确答案:89


3.

2021 是一个非常特殊的数,它可以表示成两个非负整数的平方差,2021 = 45 * 45 - 2 * 2。
2025 也是同样特殊的数,它可以表示成 2025 = 45 * 45 - 0 * 0。
请问,在 1 到 2021 中有多少个这样的数?
请注意,有的数有多种表示方法,例如 9 = 3 * 3 - 0 * 0 = 5 * 5 - 4 * 4,在算答案时只算一次。

O ( n 2 ) O(n^2) O(n2)枚举,并判断两个因数的平方差是否在[1,n]中即可。(此处n=2021)

优化:由 i 2 − ( i − 1 ) 2 ≤ n i^{2}-(i-1)^{2} \leq n i2(i1)2n解得 i ≤ n + 1 2 i \leq \frac {n+1}{2} i2n+1,因此较大的数枚举到 n + 1 2 \frac {n+1}{2} 2n+1即可。
代码:

#include 
#include 
const int M=2023;
using namespace std;
bool vis[M];
int main() {
	int res=0,n=2021,m=1;
	for(int i=1; i<(M+1)/2; i++) {
		for(int j=0; j<i; j++) {
			int now=i*i-j*j;
			if(now>=m&&now<=n&&!vis[now]) {
//				printf("%d ",now);
				res++;
				vis[now]=1;
			}
		}
	}
//	for(int i=1; i<=n; i++) {
//		if(vis[i])	printf("%d ",i);
//	}
	printf("\n%d",res);
	return 0;
}

答案:1516


4.

蓝桥杯模拟赛记录_第1张图片
哈夫曼编码,手算即可。
答案:219


5.

题意就是给一长串文本,知道有A~F六个字母,求出现次数最多的字母的出现次数。
送分题。方法:把文本复制到Word里然后Ctrl+F。
答案:78


6.

小蓝要到店里买铅笔。
铅笔必须一整盒一整盒买,一整盒 12 支,价格 p 元。
小蓝至少要买 t 支铅笔,请问他最少花多少钱?
样例输入 p=5,t=30
样例输出 15(5*3=15)

送分题,注意向上取整。

#include 
#include 

int main() {
    int p,t;
    scanf("%d%d",&p,&t);
    printf("%d",((int)ceil(t/12.0))*p);
    return 0;
}

7.

给定三个数a,b,c,问是否构成直角三角形。
样例输入:a=4 b=3 c=5
样例输出:YES
样例输入a=4 b=4 c=5
样例输出:NO

同样送分题。注意不一定按照从小到大的顺序给出,因此如果c不是最大就要交换。

#include 
#include 
#define sqr(x) ((x)*(x))
using namespace std;

int main() {
	int a,b,c;
	scanf("%d%d%d",&a,&b,&c);
	int maxn=max(max(a,b),c);
	if(c!=maxn) {
		if(a==maxn)    swap(a,c);
		else if(b==maxn)	swap(b,c);
	}
	if(sqr(a)+sqr(b)==sqr(c))
		printf("YES");
	else	printf("NO");
	return 0;
}

8.

n 个小朋友正在做一个游戏,每个人要分享一个自己的小秘密。
  每个小朋友都有一个 1 到 n 的编号,编号不重复。
  为了让这个游戏更有趣,老师给每个小朋友发了一张卡片,上面有一个 1 到 n 的数字,每个数字正好出现一次。
  每个小朋友都将自己的秘密写在纸上,然后根据老师发的卡片上的数字将秘密传递给对应编号的小朋友。如果老师发给自己的数字正好是自己的编号,这个秘密就留在自己手里。
  小朋友们拿到其他人的秘密后会记下这个秘密,老师会再指挥所有小朋友将手中的秘密继续传递,仍然根据老师发的卡片上的数字将秘密传递给对应编号的小朋友。
  这样不断重复 n 次。
  现在,每个小朋友都记下了很多个秘密。
  老师现在想找一些小朋友,能说出所有秘密,请问老师最少要找几个小朋友?

输入的第一行包含一个整数 n。
第二行包含 n 个整数 a[1], a[2], …, a[n],相邻的整数间用空格分隔,分别表示编号 1 到 n 的小朋友收到的数字。

输出一行包含一个整数,表示答案。

样例输入
6
2 1 3 5 6 4

样例输出
3

对于 30% 的评测用例,2 <= n <= 30。对于 60% 的评测用例,2 <= n <= 1000。对于所有评测用例,2 <= n <= 100000。

我们可以模拟整个游戏过程。用一个set记录某个小朋友已经知道的秘密,数组a[]记录每个小朋友当前手中是谁的的秘密。不断重复n轮。然后就可以愉快地搜索啦~
然而看到范围:
在这里插入图片描述
上面的方法光是模拟就达到 O ( n 2 ) O(n^2) O(n2),搜索复杂度更是大到没边,30%的数据都够呛。然而样例说明给了我们一些提示:
蓝桥杯模拟赛记录_第2张图片
也就是说,如果有一些小朋友互相知道对方的秘密,那么这些小朋友就可以放在一个集合中。比如下面的数据:

7
5 1 7 2 6 4 3

那么1,2,4,5,6号就互相知道对方的秘密,3,7号互相知道对方的秘密,这样就形成2个集合,只有在这两个集合里分别任意找一个人即可知道所有人的秘密。
经过上面的分析,很明显可以用并查集实现。

#include 
#include 
const int M=100001;
using namespace std;
int fa[M],a[M];
bool vis[M];
int Find(int x) {
	if(x==fa[x])	return x;
	return fa[x]=Find(fa[x]);
}

int main() {
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
		fa[i]=i;
	}
	for(int i=1; i<=n; i++) {
		int fx=Find(i),fax=Find(a[i]);
		if(fx!=fax) {
			fa[fax]=fx;
		}
	}
	int res=0;
	for(int i=1; i<=n; i++) {
		int fx=Find(i);
		if(!vis[fx]) {
			vis[fx]=1;
			res++;
		}
	}
	printf("%d",res);
	return 0;
}

9.

一个 1 到 n 的排列被称为半递增序列,是指排列中的奇数位置上的值单调递增,偶数位置上的值也单调递增。
  例如:(1, 2, 4, 3, 5, 7, 6, 8, 9) 是一个半递增序列,因为它的奇数位置上的值是 1, 4, 5, 6, 9,单调递增,偶数位置上的值是 2, 3, 7, 8,也是单调递增。
请问,1 到 n 的排列中有多少个半递增序列?
输入一行包含一个正整数 n。

输出一行包含一个整数,表示答案,答案可能很大,请输出答案除以 1000000007 的余数。

样例输入 5
样例输出 10

对于 50% 的评测用例,2 <= n <= 20。
对于所有评测用例,2 <= n <= 1000。

方法一:枚举所有排列暴力计算,复杂度O(n*n!)。
方法二:DFS枚举每个位置。
优化:不难发现偶数位置确定后,奇数位置随之确定,因此只枚举偶数位置即可。(当然只枚举奇数位置也是可以的,不过n以内偶数总不比奇数多所以还是偶数好些。)
小优化:搜索过程中不用记录当前位置,减少一个参数。(相当于把偶数位置的数“压缩”到数组最前面的位置。)
复杂度O(?),但应当高于 O ( 2 n 2 ) O(2^{\frac n2}) O(22n)不然没法解释为什么n只能到28

//只枚举偶数位置
#include 
#include 
#define MOD 1000000007
const int M=1001;
using namespace std;
//tot:总共的个数,now:当前个数,st:本轮搜索起始位置
//不用记录当前位置 
int dfs(int tot,int now=0,int st=1) {
    if(now==tot/2)    return 1;
    int res=0;
    for(int i=st; i<=tot; i++) {
    	//a[now]=i;
        res=(res+dfs(tot,now+1,i+1))%MOD;
    }
    return res;
}

int main() {
	int n;
    scanf("%d",&n);
    printf("%d",dfs(n));
	return 0;
}

方法三:由上面的思路可以更进一步发现:n个数中选取n/2个填充到偶数位置,且每种选择方案要求必须递增,那么答案很显然是 C n n 2 C_n^{\frac n2} Cn2n。由组合数性质只要递推计算出这个结果即可。

#include 
#include 
#define MOD 1000000007
const int M=1001;
using namespace std;
int combin[M][M];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=0; i<=n; i++) {
        combin[i][0]=combin[i][i]=1;
    }
    for(int i=1; i<=n; i++) {
        for(int j=1; j<i; j++) {
            combin[i][j]=(combin[i-1][j]+combin[i-1][j-1])%MOD;
        }
    }
    printf("%d",combin[n][n/2]);
	return 0;
}

10.

小蓝住在 LQ 城,今天他要去小乔家玩。
  LQ 城可以看成是一个 n 行 m 列的一个方格图。
  小蓝家住在第 1 行第 1 列,小乔家住在第 n 行第 m 列。
  小蓝可以在方格图内走,他不愿意走到方格图外。
  城市中有的地方是风景优美的公园,有的地方是熙熙攘攘的街道。小蓝很喜欢公园,不喜欢街道。他把方格图中的每一格都标注了一个属性,或者是喜欢的公园,标为1,或者是不喜欢的街道标为2。小蓝和小乔住的地方都标为了1。
  小蓝每次只能从一个方格走到同一行或同一列的相邻方格。他想找到一条路径,使得不连续走两次标为 2 的街道,请问在此前提下他最少要经过几次街道?
样例输入
3 4
1122
1221
2211
样例输出
-1
样例输入
5 6
112121
122221
221212
211122
111121
样例输出
5

对于 50% 的评测用例,2 <= n, m <= 20。
对于所有评测用例,2 <= n, m <= 300。

这题第一个样例应该是错的所以我也没拷。
也没有什么好方法,就……搜呗。

#include 
#include 
#define noexced(x,y) ((x)>=0&&(x)=0&&(y)
const int M=301,dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
using namespace std;
int n,m,res=M*M;
bool vis[M][M];
char s[M][M];
bool dfs(int x,int y,int street=0) {
	if(x==n-1&&y==m-1) {
		res=min(res,street);
		return 1;
	}
	vis[x][y]=1;
	bool f=0;
	for(int i=0; i<4; i++) {
		int nx=x+dx[i],ny=y+dy[i];
		if(noexced(nx,ny)&&!vis[nx][ny]) {
			if(s[x][y]=='1') {
				if(dfs(nx,ny,street))	f=1;
			}
			else if(s[x][y]=='2'&&s[nx][ny]!='2') {
				if(dfs(nx,ny,street+1))	 f=1;
			}
		}
	}
	vis[x][y]=0;
	return f;
}

int main() {
	scanf("%d%d",&n,&m);
	for(int i=0; i<n; i++)    scanf("%s",s[i]);
	(dfs(0,0))?printf("%d",res):printf("-1");
	return 0;
}

总体评价:个人认为难度对应洛谷中的级别:

1、3、5~ 7:红
2、9:橙
4、8:黄
10:绿~蓝
还是挺简单的。

你可能感兴趣的:(数据结构与算法,蓝桥杯,算法)