模运算专题练习 ——基于罗勇军老师的《蓝桥杯算法入门C/C++》
目录
一、1.刷题统计 - 蓝桥云课
算法代码:
代码思路分析
二、1.倍数问题 - 蓝桥云课
算法代码:
代码思路
关键逻辑详解
一、1.刷题统计 - 蓝桥云课
算法代码:
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间,避免每次调用标准库函数时写 std::
typedef long long ll; // 定义 ll 为 long long 类型的别名,方便使用
int main() {
// 定义变量 a(工作日每天完成的任务数)、b(周末每天完成的任务数)、n(总任务数)
ll a, b, n;
// 从标准输入读取 a、b、n 的值
cin >> a >> b >> n;
// 计算一周(7 天)完成的任务量:5 个工作日每天完成 a 个任务,2 个周末每天完成 b 个任务
ll week = a * 5 + b * 2;
// 计算完整周的天数:总任务数除以一周的任务量,取整后乘以 7
ll days = (n / week) * 7;
// 计算剩余的任务量:总任务数对一周的任务量取模
ll k = n % week;
// 如果有剩余任务需要处理
if (k <= a * 5) {
// 如果剩余任务可以在 5 个工作日内完成
// 计算需要多少个工作日完成剩余任务
days += k / a;
// 如果剩余任务不能整除 a,则需要额外一天
if (k % a) {
days += 1;
}
} else {
// 如果剩余任务需要跨到周末完成
// 先完成 5 个工作日
days += 5;
// 计算剩余任务量:减去 5 个工作日完成的任务量
k -= a * 5;
// 计算需要多少个周末完成剩余任务
days += k / b;
// 如果剩余任务不能整除 b,则需要额外一天
if (k % b) {
days += 1;
}
}
// 输出总天数
cout << days;
// 主函数返回 0,表示程序正常结束
return 0;
}
代码思路分析
二、1.倍数问题 - 蓝桥云课
算法代码:
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间,避免每次调用标准库函数时写 std::
int m[1050][3]; // 定义二维数组 m,用于存储每个余数对应的最大三个数
int u[100010]; // 定义数组 u,用于存储输入的整数
int main() {
int n, k; // 定义变量 n(数组大小)和 k(模数)
cin >> n >> k; // 输入 n 和 k
// 输入数组 u
for (int i = 0; i < n; i++) {
scanf("%d", &u[i]);
}
// 将数组 u 按从大到小排序
sort(u, u + n, greater<int>());
// 遍历数组 u,按模数分类
for (int i = 0; i < n; i++) {
int y = u[i] % k; // 计算当前数对 k 取模的结果
// 将当前数存储到 m[y] 中,最多存储三个数
if (m[y][0] == 0) {
m[y][0] = u[i];
} else if (m[y][1] == 0) {
m[y][1] = u[i];
} else if (m[y][2] == 0) {
m[y][2] = u[i];
}
}
int ans = 0; // 定义变量 ans,用于存储最终的最大和
// 枚举可能的和 x(x 是 k 的倍数,范围为 0 到 2k)
for (int x = 0; x <= 2 * k; x += k) {
// 枚举余数 a
for (int a = 0; a < k; a++) {
// 枚举余数 b
for (int b = 0, c; b < k; b++) {
// 计算余数 c = x - a - b
c = x - a - b;
// 检查 c 是否合法(即 c 在 0 到 k-1 之间)
if (c >= 0 && c < k) {
// 计算当前组合的和,并更新最大值
ans = max(ans, m[a][0] + m[b][a == b] + m[c][(a == c) + (b == c)]);
}
}
}
}
// 输出结果
cout << ans;
return 0; // 主函数返回 0,表示程序正常结束
}
代码思路
这段代码的目的是从一组整数中选出 最多三个数,使得它们的和是 k 的倍数,并且和尽可能大。具体思路如下:
-
输入数据:
-
读取整数 n(数组大小)和 k(模数)。
-
读取数组 u,存储 n 个整数。
-
-
排序数组:
-
将数组 u 按从大到小排序,方便后续优先选择较大的数。
-
-
按模数分类:
-
使用二维数组 m 记录每个余数对应的最大三个数。
-
遍历数组 u,计算每个数对 k 取模的结果 y=u[i]%k。
-
将数 u[i] 存储到 m[y]中,最多存储三个数(即每个余数最多记录三个最大的数)。
-
-
枚举可能的和:
-
枚举可能的和 x,其中 x 是 k 的倍数,范围为 0 到2k(因为最多选三个数,三个数的和对 k 取模的范围是 0 到 2k)。
-
对于每个 x,枚举两个余数 a 和 b,计算第三个余数 c=x−a−b。把题目转化为a%k有k种取值,b%k也有k种取值,而选定a、b之后,可以通过a、b、x计算出c,所以只需要枚举a%k和b%k即可,计算复杂度是O(k**2),能通过100%的测试。
-
检查 c 是否合法(即 c 在 0 到 k−1 之间)。
-
如果合法,计算当前组合的和,并更新最大值。
-
-
输出结果:
-
输出满足条件的最大和。
-
关键逻辑详解
-
按模数分类:
-
使用二维数组 m 记录每个余数对应的最大三个数。
-
例如,m[y][0] 存储余数为 y 的最大数,m[y][1] 存储次大数,m[y][2] 存储第三大数。
-
-
枚举可能的和:
-
由于最多选三个数,三个数的和对 k 取模的范围是 0 到 2k。
-
对于每个可能的和 x,枚举两个余数 a 和 b,计算第三个余数 c=x−a−b。
-
检查 c是否合法(即 c在 0 到 k−1 之间)。
-
-
计算当前组合的和:
-
使用 m[a][0] 获取余数为 a 的最大数。
-
使用 m[b][a==b] 获取余数为 b 的数:
-
如果 a==b,则选择次大数 m[b][1]。
-
否则,选择最大数 m[b][0]。
-
-
使用 m[c][(a==c)+(b==c)]获取余数为 c 的数:
-
如果 a==c或 b==c,则需要跳过已选择的数。
-
-
-
详细解析:m[a][0]
:-
表示余数为
a
的最大数。 -
例如,如果
a = 1
,则m[1][0]
是余数为 1 的最大数。
-
-
m[b][a == b]
:-
表示余数为
b
的数。 -
如果
a == b
,说明余数为b
的数已经被选过一次(作为m[a][0]
),因此需要选择次大数m[b][1]
。 -
如果
a != b
,则选择最大数m[b][0]
。
-
-
m[c][(a == c) + (b == c)]
:-
表示余数为
c
的数。 -
如果
a == c
或b == c
,说明余数为c
的数已经被选过,因此需要跳过已选择的数。 -
(a == c) + (b == c)
的值可能是 0、1 或 2:-
如果是 0,选择最大数
m[c][0]
。 -
如果是 1,选择次大数
m[c][1]
。 -
如果是 2,选择第三大数
m[c][2]
。
-
-
-
max(ans, ...)
:-
将当前组合的和与
ans
比较,取较大值更新ans
。
-
-
更新最大值:
-
使用
max
函数更新当前最大值。
-