填空题
第一题
问题描述
小蓝的IP地址为 192.168.*.21,其中 * 是一个数字,请问这个数字最大可能是多少?
思路:
常识
答案:255
第二题
问题描述
如果一个整数 g 能同时整除整数 A 和 B,则称 g 是 A 和 B 的公约数。例如:43 是 86 和 2021 的公约数。 请问在 1(含) 到 2021(含) 中,有多少个数与 2021 存在大于 1 的公约数。请注意 2021 和 2021 有大于 1 的公约数,因此在计算的时候要算一个。
思路:
遍历2~2021,枚举每个数与2021是否有非1的公约数。
答案:89
#include<iostream>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
const int maxn = 300 + 5;
int arr[maxn];
inline int gcd(int a, int b) {
return b > 0 ? gcd(b, a % b) : a;
}
void solve() {
int ans = 0;
for (int i = 2; i <= 2021; i++) {
int temp = gcd(i, 2021);
if (temp != 1)ans++;
}
cout << ans << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第三题
问题描述
2021 是一个非常特殊的数,它可以表示成两个非负整数的平方差,2021 = 45 * 45 - 2 * 2。 2025 也是同样特殊的数,它可以表示成 2025 = 45 * 45 - 0 * 0。 请问,在 1 到 2021 中有多少个这样的数? 请注意,有的数有多种表示方法,例如 9 = 3 * 3 - 0 * 0 = 5 * 5 - 4 * 4,在算答案时只算一次。
思路:
设 x = i * i - j * j,当 j 小于 i 时,x为正数。则暴力循环i,j即可。因为i2 - j2 = (i + j) - (i - j)。当 i 确定时,j = i - 1 为最小正数,即(i + i - 1)(i - i - 1) = 2i - 1 = 2021。 i = 1010。则为i的枚举范围。
答案:1516
#include<iostream>
#include<cstring>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
const int maxn = 2021 + 5;
int arr[maxn];
void solve() {
memset(arr, 0, sizeof arr);
for (int i = 1; i <= 1011; i++) {
for (int j = 0; j < i; j++) {
int temp = i * i - j * j;
if (temp <= 2021)arr[temp]++;
}
}
int ans = 0;
for (int i = 1; i <= 2021; i++) {
if (arr[i] != 0)ans++;
}
cout << ans << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第四题
问题描述
小蓝要用01串来表达一段文字,这段文字包含 a, b, c, d, e, f 共 6 个字母,每个字母出现的次数依次为:a 出现 10 次,b 出现 20 次,c 出现 3 次,d 出现 4 次,e 出现 18 次,f 出现 50 次。 小蓝准备分别对每个字母使用确定的01串来表示,不同字母的01串长度可以不相同。 在表示文字时,将每个字母对应的01串直接连接起来组成最终的01串。为了能够正常还原出文字,小蓝的编码必须是前缀码,即任何一个字符对应的01串都不能是另一个字符对应的01串的前缀。 例如,以下是一个有效的编码: a: 000 b: 111 c: 01 d: 001 e: 110 f: 100 其中 c 的长度为 2,其它字母的编码长度为 3,这种方式表示这段文字需要的总长度为:10 * 3 + 20 * 3 + 3 * 2 + 4 * 3 + 18 * 3 + 50 * 3 = 312。 上面的编码显然不是最优的,将上面的 f 的编码改为 10,仍然满足条件,但是总长度为 262,要短 50。 要想编码后的总长度尽量小,应当让出现次数多的字符对应的编码短,出现次数少的字符对应的编码长。 请问,在最优情况下,编码后的总长度最少是多少?
思路:
哈夫曼树
答案:219
#include<iostream>
#include<vector>
#include<queue>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
const int maxn = 2021 + 5;
void solve() {
int arr[10] = { 10,20,3,4,18,50 };
priority_queue<int, vector<int>, greater<int> > pq;
for (int i = 0; i < 6; i++) pq.push(arr[i]);
int ans = 0;
while (pq.size() > 1) {
int min1 = pq.top(); pq.pop();
int min2 = pq.top(); pq.pop();
ans += min1 + min2;
pq.push(min1 + min2);
}
cout << ans << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第五题
问题描述
下面的矩阵中包含 ABCDEF 六种字符,请问出现最多的字符出现了几次? FFEEFEAAECFFBDBFBCDA DACDEEDCCFFAFADEFBBA FDCDDCDBFEFCEDDBFDBE EFCAAEECEECDCDECADDC DFAEACECFEADCBFECADF DFBAAADCFAFFCEADFDDA EAFAFFDEFECEDEEEDFBD BFDDFFBCFACECEDCAFAF EFAFCDBDCCBCCEADADAE BAFBACACBFCBABFDAFBE FCFDCFBCEDCEAFBCDBDD BDEFCAAAACCFFCBBAAEE CFEFCFDEEDCACDACECFF BAAAFACDBFFAEFFCCCDB FADDDBEBCBEEDDECFAFF CDEAFBCBBCBAEDFDBEBB BBABBFDECBCEFAABCBCF FBDBACCFFABEAEBEACBB DCBCCFADDCACFDEDECCC BFAFCBFECAACAFBCFBAF
思路:
map直接跑一遍就行了
答案:78
#include<iostream>
#include<string>
#include<map>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
const int maxn = 2021 + 5;
void solve() {
map<char, int> msi;
string str;
while (getline(cin, str)) {
for (auto i : str) {
msi[i]++;
}
}
int maxnum = 0;
for (auto it : msi) {
maxnum = max(it.second, maxnum);
}
cout << maxnum << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
编程题
第六题
问题描述
小蓝要到店里买铅笔。 铅笔必须一整盒一整盒买,一整盒 12 支,价格 p 元。 小蓝至少要买 t 支铅笔,请问他最少花多少钱?
输入格式
输入一行包含两个整数 p、t,用一个空格分隔。
输出格式
输出一行包含一个整数,表示答案。
样例输入
5 30
样例输出
15
样例说明
小蓝至少要买3盒才能保证买到30支铅笔,总共花费 15 元。
评测用例规模与约定
对于所有评测用例,1 <= p <= 100,1 <= t <= 10000。
思路:
设要买的盒数为n,则12 * n ≥ t,所以n = [t / 12]
代码:
#include<iostream>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
void solve() {
int p, t; cin >> p >> t;
if (t % 12) {
cout << p * (t / 12) + p << endl;
}
else cout << p * (t / 12) << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第七题
问题描述
给定一个三角形的三条边的长度 a, b, c,请问这个三角形是不是一个直角三角形。
输入格式
输入一行包含三个整数 a, b, c,表示三角形三边的长度,相邻整数之间用一个空格分隔。
输出格式
如果是直角三角形,输出“YES”(全大写),否则输出“NO”(全大写)。
样例输入
3 4 5
样例输出
YES
样例输入
4 5 4
样例输出
NO
评测用例规模与约定
对于所有评测用例,1 <= a, b, c <= 1000。
思路:
勾股定理
代码:
#include<iostream>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
bool isRight(int a,int b,int c) {
if (a * a + b * b == c * c)return true;
else return false;
}
void solve() {
int a, b, c;
cin >> a >> b >> c;
bool ans = false;
if (a > b && a > c) ans = isRight(b, c, a);
else if (b > a && b > c)ans = isRight(a, c, b);
else if (c > a && c > b)ans = isRight(a, b, c);
if (ans)cout << "YES" << endl;
else cout << "NO" << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第八题
问题描述
n 个小朋友正在做一个游戏,每个人要分享一个自己的小秘密。 每个小朋友都有一个 1 到 n 的编号,编号不重复。 为了让这个游戏更有趣,老师给每个小朋友发了一张卡片,上面有一个 1 到 n 的数字,每个数字正好出现一次。 每个小朋友都将自己的秘密写在纸上,然后根据老师发的卡片上的数字将秘密传递给对应编号的小朋友。如果老师发给自己的数字正好是自己的编号,这个秘密就留在自己手里。 小朋友们拿到其他人的秘密后会记下这个秘密,老师会再指挥所有小朋友将手中的秘密继续传递,仍然根据老师发的卡片上的数字将秘密传递给对应编号的小朋友。 这样不断重复 n 次。 现在,每个小朋友都记下了很多个秘密。 老师现在想找一些小朋友,能说出所有秘密,请问老师最少要找几个小朋友?
输入格式
输入的第一行包含一个整数 n。 第二行包含 n 个整数 a[1], a[2], ..., a[n],相邻的整数间用空格分隔,分别表示编号 1 到 n 的小朋友收到的数字。
输出格式
输出一行包含一个整数,表示答案。
样例输入
6 2 1 3 5 6 4
样例输出
3
样例说明
最终小朋友 1, 2 互相知道了对方的秘密,小朋友 3 只知道自己的秘密,小朋友 4, 5, 6 互相知道了对方的秘密。 至少要找 3 个小朋友才能说出所有秘密。
评测用例规模与约定
对于 30% 的评测用例,2 <= n <= 30。 对于 60% 的评测用例,2 <= n <= 1000。 对于所有评测用例,2 <= n <= 100000。
思路:
并查集
代码:
#include<iostream>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int arr[maxn], tree[maxn];
int find(int x) {
if (tree[x] == x)return x;
else return find(tree[x]);
}
void build(int x,int y) {
int fx = find(x);
int fy = find(y);
tree[fx] = fy;
}
void solve() {
int n; cin >> n;
for (int i = 1; i <= n; i++) {
tree[i] = i;
cin >> arr[i];
}
for (int i = 1; i <= n; i++) {
int x = i, y = arr[i];
build(x, y);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (tree[i] == i)ans++;
}
cout << ans << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第九题
问题描述
一个 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
样例说明
有以下半递增序列: (1, 2, 3, 4, 5) (1, 2, 3, 5, 4) (1, 2, 4, 3, 5) (1, 3, 2, 4, 5) (1, 3, 2, 5, 4) (1, 4, 2, 5, 3) (2, 1, 3, 4, 5) (2, 1, 3, 5, 4) (2, 1, 4, 3, 5) (3, 1, 4, 2, 5)
评测用例规模与约定
对于 50% 的评测用例,2 <= n <= 20。 对于所有评测用例,2 <= n <= 1000。
思路:
对n个数进行半递增排序,因为奇数和偶数上的数为递增,则当确定好奇数或者偶数上有哪些数时,递增序列有且仅有一种,并且当奇数位置的数确定时,偶数位置上的数也确定了。也就是我们可以任意的从n个数中取出m个数来确定奇数或者偶数的数,就找到一种半递增序列。这就成为了Cnm组合问题了。因为n最大为1000,远远超出int,并且还需要取模,所以这里用杨辉三角来计算组合问题。
代码:
#include<iostream>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int maxn = 1e3 + 5;
int arr[maxn];
void solve() {
int n; cin >> n;
int m = n >> 1;
for (int i = 1; i <= n; i++) {
arr[0] = 1, arr[i] = 1;
for (int j = i - 1; j > 0; j--) {
arr[j] = (arr[j] + arr[j - 1]) % mod;
}
}
cout << arr[m] << endl;
}
int main() {
ios, tie;
solve();
return 0;
}
第十题
问题描述
小蓝住在 LQ 城,今天他要去小乔家玩。 LQ 城可以看成是一个 n 行 m 列的一个方格图。 小蓝家住在第 1 行第 1 列,小乔家住在第 n 行第 m 列。 小蓝可以在方格图内走,他不愿意走到方格图外。 城市中有的地方是风景优美的公园,有的地方是熙熙攘攘的街道。小蓝很喜欢公园,不喜欢街道。他把方格图中的每一格都标注了一个属性,或者是喜欢的公园,标为1,或者是不喜欢的街道标为2。小蓝和小乔住的地方都标为了1。 小蓝每次只能从一个方格走到同一行或同一列的相邻方格。他想找到一条路径,使得不连续走两次标为 2 的街道,请问在此前提下他最少要经过几次街道?
输入格式
输入的第一行包含两个整数 n, m,用一个空格分隔。 接下来 n 行,每行一个长度为 m 第数字串,表示城市的标注。
输出格式
输出一行包含一个整数,表示答案。如果没有满足条件的方案,输出 -1。
样例输入
3 4 1121 1211 2211
样例输出
1
样例输入
3 4 1122 1221 2211
样例输出
-1
样例输入
5 6 112121 122221 221212 211122 111121
样例输出
5
评测用例规模与约定
对于 50% 的评测用例,2 <= n, m <= 20。 对于所有评测用例,2 <= n, m <= 300。
思路:
用优先队列,将经过街区少的走法设为优先走法,这样找出来的第一个到达终点的就是最优解。使用普通队列的走法找出来的只是最先到达终点的走法,不一定是经过街区最少的走法
代码:
#include<iostream>
#include<cstring>
#include<queue>
#define ios ios::sync_with_stdio(false)
#define tie cin.tie(nullptr),cout.tie(nullptr)
#define endl '\n'
using namespace std;
const int inf = 0x3f3f3f3f;
using pii = pair<int, int>;
const int mod = 1000000007;
const int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
const int maxn = 300 + 5;
char maze[maxn][maxn];
bool vis[maxn][maxn];
int n, m, ans = inf;
class Node {
public:
int x, y, count;
Node(int x,int y,int count) {
this->x = x, this->y = y, this->count = count;
}
friend bool operator <(const Node& a, const Node& b) {
return a.count > b.count;
}
};
int bfs(int x,int y,int count) {
priority_queue<Node> pq;
pq.push(Node(x, y, count));
while (!pq.empty()) {
Node tp = pq.top(); pq.pop();
for (int i = 0; i < 4; i++) {
int tx = tp.x + dir[i][0];
int ty = tp.y + dir[i][1];
if (tx > 0 && tx <= n && ty > 0 && ty <= m) {
if (tx == n && ty == m) {
vis[tx][ty] = true;
return tp.count;
}
if (maze[tp.x][tp.y] == '2' && maze[tx][ty] == '2')continue;
if (vis[tx][ty])continue;
if (maze[tx][ty] == '2')pq.push(Node(tx, ty, tp.count + 1));
else pq.push(Node(tx, ty, tp.count));
vis[tx][ty] = true;
}
}
}
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> (maze[i] + 1);
}
memset(vis, false, sizeof vis);
int ans = bfs(1, 1, 0);
if (vis[n][m])cout << ans << endl;
else cout << -1 << endl;
}
int main() {
ios, tie;
solve();
return 0;
}