非官方题解,仅供参考,不保证全对
试题A:字节计算
描述:
在计算机存储中,12.5MB是多少字节?
题解:
直接用12.5*1024*1024即可
答案:
13107200
试题B:合法括号序列
描述:
由1对括号,可以组成一种合法括号序列:。
由2对括号,可以组成两种合法括号序列:。
由4对括号组成的合法括号序列一共有多少种?
题解:
其实合法的数量不多,如果自己枚举可以依次列出
如果列的话感觉比较麻烦,可以写一个程序跑一下
4对括号一共8个,用1代表左括号,0代表右括号
就可以转化为8位二进制,枚举这8位二进制的所有情况
对每种情况用栈进行括号匹配即可
想要更快的可以使用一下__builtin_popcount()函数优化,表示二进制数中有多少个1,因为匹配必须左右括号各4位,必须让这个等于4
答案:
14
代码
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<iomanip> #include<stdlib.h> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<string.h> #include<math.h> #include<set> #include<numeric> #include<cassert> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e5+10; bool check(int x){ stack<int> s; for(int i=0;i<8;i++){ if((x>>i)&1)s.push(1); else{ if(s.empty())return false; else s.pop(); } } return true; } int main(){ int ans=0; for(int i=1;i<1<<8;i++) if(check(i)&&__builtin_popcount(i)==4)ans++; printf("%d",ans); return 0; }
试题C:蓝桥单词
描述:
将LANQIAO中的字母重新排列,可以得到不同的单词,如LANQIAO、AAILNOQ等,注意这77个字母都要被用上,单词不一定有具体的英文意义。
请问,总共能排列如多少个不同的单词。
题解:
观察一下,其实就是个排列组合题
但是LANQIAO里有两个A是重复的,选一下A的位置就是
然后再排列剩下的5个字母的位置即可即
相乘即可,计算器算一下就行了
答案:
2520
试题D:无向连通图
描述:
一个包含有2019个结点的无向连通图,最少包含多少条边?
题解:
最少的边其实就是形成一个最小连通图
其实就是使图成为一棵树即可,n个点的树有n-1条边
答案:
2018
试题E:反倍数
描述:
给定三个整数 a, b, c如果一个整数既不是a 的整数倍也不是b 的整数倍还不是c的整数倍,则这个数称为反倍数。
请问在1 至 n 中有多少个反倍数。
输入:
输入的第一行包含一个整数n。
第二行包含三个整数 a, b, c,相邻两个数之间用一个空格分隔。
输出:
输出一行包含一个整数,表示答案。
样例:
输入:
30
2 3 6
输出:
10
提示:
样例说明
以下这些数满足要求:1, 5, 7, 11, 13, 17, 19, 23, 25, 29
评测用例规模与约定
对于40% 的评测用例,1 <= n <= 10000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000,1 <= a <= n,1 <= b <= n,1 <= c <= n
题解:
满分的数据范围是1e6,所以直接枚举每个数即可
从1到n,每个数对a,b,c取余只要都不为0就是反倍数
代码
#include<bits/stdc++.h> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e5+10; int main(){ int n,a,b,c,ans=0; cin>>n>>a>>b>>c; for(int i=1;i<=n;i++) if(i%a&&i%b&&i%c)ans++; cout<<ans<<endl; return 0; }
试题F:单词加密
描述:
给定一个单词,请使用凯撒密码将这个单词加密。
凯撒密码是一种替换加密的技术,单词中的所有字母都在字母表上向后偏移33位后被替换成密文。即a变为d,b变为e,...,w变为z,x变为a,y变为b,z变为c。
例如,lanqiao会变成odqtldr。
输入:
输入一行,包含一个单词,单词中只包含小写英文字母。
输出:
输出一行,表示加密后的密文。
样例:
输入:
lanqiao
输出:
odqtldr
评测用例规模与约定
对于所有评测用例,单词中的字母个数不超过100。
题解:
字母个数不多,直接枚举每个改变
可以考虑直接+3的情况,但是如果这种情况就需要特判xyz
所以可以先让每个字母减去a的ASCII码
这样每个字母就会变成0到26,然后+3后再向26取余,再加上a的ASCII码,就可以不需要特判了
代码
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<iomanip> #include<stdlib.h> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<string.h> #include<math.h> #include<set> #include<numeric> #include<cassert> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e5+10; int main(){ string s; cin>>s; for(int i=0;i<s.length();i++){ if('a'<=s[i]&&s[i]<='z') s[i]=(s[i]-'a'+3)%26+'a'; if(s[i]>='A'&&s[i]<='Z')//题目保证只有小写,这段可有可无 s[i]=(s[i]-'A'+3)%26+'A';//只是为了防止有大写的情况 } cout<<s<<endl; return 0; }
试题G:螺旋矩阵
描述:
对于一个n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
例如,一个 4 行 5 列的螺旋矩阵如下:
1 2 3 4 5 14 15 16 17 6 13 20 19 18 7 12 11 10 9 8
输入:
输入的第一行包含两个整数 n, m分别表示螺旋矩阵的行数和列数。
第二行包含两个整数 r, c表示要求的行号和列号。
输出:
输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。
样例:
输入:
4 5
2 2
输出:
15
评测用例规模与约定
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= r <= n,1 <= c <= m。
题解:
最直接的方法,直接先把整个题目要求的图绘制出来
可以用while一直走,把走过的位置标记
每次往右下左上循环一直走,每个方向走到边界或者被标记的位置停下,然后直接用r,c找对应位置即可
代码
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<iomanip> #include<stdlib.h> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<string.h> #include<math.h> #include<set> #include<numeric> #include<cassert> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e5+10; int g[1010][1010]; int main(){ int n,m,r,c; cin>>n>>m>>r>>c; int sum=n*m,cnt=1,row=0,col=0; g[row][col]=1; while(cnt<sum){ while(col+1<m&&!g[row][col+1])g[row][++col]=++cnt; while(row+1<n&&!g[row+1][col])g[++row][col]=++cnt; while(col-1>=0&&!g[row][col-1])g[row][--col]=++cnt; while(row-1>=0&&!g[row-1][col])g[--row][col]=++cnt; } cout<<g[r-1][c-1]<<endl; return 0; }
试题H:摆动序列
描述:
如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。
即
小明想知道,长度为 m,每个数都是 1 到n 之间的正整数的摆动序列一共有多少个。
输入:
输入一行包含两个整数 m,n。
输出:
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
样例:
输入:
3 4
输出:
14
提示:
样例说明: 以下是符合要求的摆动序列: 2 1 2 2 1 3 2 1 4 3 1 2 3 1 3 3 1 4 3 2 3 3 2 4 4 1 2 4 1 3 4 1 4 4 2 3 4 2 4 4 3 4
评测用例规模与约定
对于 20% 的评测用例,1 <= n, m <= 5;
对于 50% 的评测用例,1 <= n, m <= 10;
对于 80% 的评测用例,1 <= n, m <= 100;
对于所有评测用例,1 <= n, m <= 1000。
题解:
这道题最直接的方法是用dfs一位一位搜索
如果这一位是奇数搜索小于前一位的否则搜索大于前一位的
但是这样的话,复杂度是阶乘级别,肯定会超时
然后我们可以想到用记忆化搜索优化dfs
用一个记忆数组维护一下长度为i最后一位是j的情况有多少种
但是这样优化还是比较慢,因为每次不能借用前一种的情况
最后我们再想,可以采用二维dp的方法 跟上种方法类似,表示长度为i最后一位<=j(奇数是小于等于,偶数是大于等于)的情况有多少种
然后每次的更新利用上一次更新做到优化
最后可以使用复杂度完成
i和j对应的n和m,n和m都是1e3,所以足够开一个二维数组
然后开始找状态转移方程
如果i是奇数,说明他要找比上一个数他小的情况
就加上,
让 j 从 m 到 0 枚举,然后每次要加上一次更新的情况()
如果i是偶数,就要找比一个数大的情况
也就是
让j 从 0 到 m 枚举,然后每次更新上一次更新的情况()
所以可以找到状态方程
代码
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<iomanip> #include<stdlib.h> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<string.h> #include<math.h> #include<set> #include<numeric> #include<cassert> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e4; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e5+10; int dp[1010][1010]; int main(){ int n,m; cin>>n>>m; for(int i=1;i<=m;i++)dp[1][i]=m-i+1;//初始化 第一位为i的时候有m-i+1种情况 for(int i=2;i<=n;i++){ if(i&1)for(int j=m;j>=1;j--)dp[i][j]=(dp[i-1][j-1]+dp[i][j+1])%mod; else for(int j=1;j<=m;j++)dp[i][j]=(dp[i-1][j+1]+dp[i][j-1])%mod; } //从上面的更新策略和数组含义,奇数是倒着更新,偶数是正着 //所以可以得到下面的输出方法 if(n&1)cout<<dp[n][1]<<endl; else cout<<dp[n][m]<<endl; return 0; }
试题I:村庄建设
描述:
2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。
这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。
现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。
小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 高度为的村庄与坐标为高度为 的村庄之间连接的费用为 请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。
由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。
输入:
输入的第一行包含一个整数 n ,表示村庄的数量。
接下来 n 行,每个三个整数 x, y, h分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。
输出:
输出一行,包含一个实数,四舍五入保留 2位小数,表示答案。
样例:
输入:
4
1 1 3
9 9 7
8 8 6
4 5 4
输出:
17.41
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。
题解:
很明显,这是一道最小生成树的模版题
1-n任意两个点之间的边为题目的给出的式子
然后可以建邻接矩阵用Prim写
也可以用结构体存边用Kruskal写
(具体模版代码可以百度查到)
我这里的代码直接用的是Kruskal
代码
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<iomanip> #include<stdlib.h> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<string.h> #include<math.h> #include<set> #include<numeric> #include<cassert> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e6+10; struct edge{ int u,v; double w; }e[maxn]; bool cmp(edge a,edge b){ return a.w<b.w; } int fa[1010]; double x[1010],y[1010],z[1010]; int find(int x){ if(fa[x]==x)return x; return fa[x]=find(fa[x]); } double dis(int i,int j){ return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))+(z[i]-z[j])*(z[i]-z[j]); } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lf%lf%lf",&x[i],&y[i],&z[i]); fa[i]=i; } int cnt=0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) e[cnt].u=i,e[cnt].v=j,e[cnt++].w=dis(i,j); sort(e,e+cnt,cmp); double ans=0; for(int i=0;i<cnt;i++){ int p=find(e[i].u),q=find(e[i].v); if(p==q)continue; ans+=e[i].w; fa[q]=p; } printf("%.2f",ans); return 0; }
试题J:郊外植树
描述:
小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。
小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,总共 n个。他们准备把自己带的树苗都植下去。
然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。
他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。
小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。他们同时希望这些树所能覆盖的面积和(圆面积和)最大。
输入:
输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。
接下来 n行,每行三个整数 x, y, r表示一棵树在空地上的横、纵坐标和半径。
输出:
输出一行包含一个整数,表示在不冲突下可以植树的面积和。由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。
样例:
输入:
6
1 1 2
1 4 2
1 7 2
4 1 2
4 4 2
4 7 2
输出:
12
样例解释
如图所示,选择三个红色的圆,或者三个绿色的圆,能够获得最大的面积
评测用例规模与约定
对于30% 的评测用例,1 <= n <= 101<=n<=10;
对于 60% 的评测用例,1 <= n <= 20;
对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。
题解:
对于这种题,最先想到的方法肯定是二进制枚举
但是二进制枚举,直接用二进制1表示存在,0表示不存在
判断是否矛盾,矛盾的continue,否则计数取最大
但是很明显,n最大是30,如果用二进制那就是2的30次方
这种情况肯定要超时了
所以我们需要去掉一些明显不对的情况
那么只能用dfs进行剪枝(设置一个估价函数,方法类似)
我们可以把这些数按半径从大到校排序,因为半径平方就是面积,会为最后的ans做贡献
然后我们需要设置一个估价函数
这个估价函数代表的是从某棵树开始,后面的树全部种下,能为最后结果贡献多少值
其实就是一个后缀的面积和
倒着循环一次就可以枚举出来了
然后我们开始DFS操作
DFS每次搜索都维护当前到达到第几个,当前面积和当前状态
当前状态依旧是用二进制维护
对于每搜索到一个新的树,就将他和之前的树,也就是二进制中的1进行判断,看是否能够同时种,如果和之前的所有树都可同时种
那么搜索一种种下这棵树的情况
然后对于任何一棵树 都搜索一种不种该树的情况
这样就可以枚举出来所有的情况了
每次搜索完所有的树,进行一次更新,将答案最大化
然后用刚才的估价函数进行估价,如果后面的树全部种下仍小于答案,那么直接停止该次搜索
最后得到答案输出即可
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<iomanip> #include<stdlib.h> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<string.h> #include<math.h> #include<set> #include<numeric> #include<cassert> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define all(x) (x).begin(),(x).end() #define endl '\n' typedef long long ll; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-8; const int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; const int maxn=1e6+10; int n; struct node{ int x,y,r; }p[100]; bool cmp(node a,node b){ return a.r>b.r; } int sum[100],ans; map<int,int> m; bool b[100][100]; //三个形参分别表示 //第cur个,此时面积是y,状态是s //s代表的是一个二进制01状态 void dfs(int cur, int tot, int st) { if(cur>n){//全部搜索一遍后 ans=max(ans,tot);//取最大结果 return; } //估价函数判断现在是否能结束 if(ans>sum[cur]+tot)return; bool f=0; //i&(-i)表示二进制的最后一位 for(int i=st,j=i&(-i);i;i-=j,j=i&(-i)){ if (b[cur][m[j]])f=1; if (f)break; } if (!f)dfs(cur+1,tot+p[cur].r,st|(1<<cur)); dfs(cur+1,tot,st); } int dis(int i,int j){ return (p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].r); m[1<<i]=i;//维护2的i次方对应的数的大小 } sort(p+1,p+1+n,cmp); for (int i=1;i<=n;i++) for(int j=1;j<=n;j++) if (dis(i,j)<(p[i].r+p[j].r)*(p[i].r+p[j].r)) b[i][j] = b[j][i] = 1; for(int i=1;i<=n;i++)p[i].r*=p[i].r;//将半径转化为面积,以便操作 //维护面积后缀和,作为估价函数进行剪枝 for (int i=n;i;i--)sum[i]=sum[i+1]+p[i].r; dfs(1,0,0); printf("%d",ans); return 0; }