非官方题解,仅供参考,不保证全对


试题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;
}