3 条题解
-
0
暴力模拟法
好的,教练来提供一种最直观、最不需要动脑筋数学公式的暴力模拟法。
这种方法的核心思想是:不要去想取模公式,超出了就减去 N,不够了就加上 N,直到下标合法为止。
这种方法特别适合初学者理解“环”的概念,且在本题的数据范围()下完全能通过,不会超时。
暴力解法思路
- 用一个变量
pos记录当前位置下标。 - 每次读入一个移动步数
k,直接执行pos = pos + k。 - 处理越界(把数组变成环):
- 如果
pos太大了():说明走过了终点,那就不断减去N,直到它回到 范围内。 - 如果
pos变成负数了():说明反向走过头了,那就不断加上N,直到它变成正数且在 范围内。
- 如果
C++14 代码实现
#include <iostream> #include <vector> using namespace std; int main() { // 1. 输入处理 int n, m; if (!(cin >> n >> m)) return 0; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } // 2. 暴力模拟游走 int pos = 0; // 初始在下标 0 for (int i = 0; i < m; i++) { int k; cin >> k; // 简单粗暴:直接加 pos = pos + k; // 修正:如果跑太远了(下标超过 N-1),就拉回来 // 比如 N=5,pos跑到 7,减去 5 变成 2 while (pos >= n) { pos = pos - n; } // 修正:如果跑到负数去了,就推回去 // 比如 N=5,pos跑到 -1,加上 5 变成 4 while (pos < 0) { pos = pos + n; } } // 3. 输出结果 cout << a[pos] << endl; return 0; }为什么这个方法可行?(复杂度分析)
- 题目数据范围:, 每次移动步数 。
- 最坏情况:
- 如果
k是100,N是1,那么while (pos >= n)循环会执行 100 次。 - 总共有
M() 个指令。 - 总运算次数大约是 次。
- 如果
- 结论:计算机每秒可以运行 次运算,一万次运算对它来说就像眨眼一样快。所以这个暴力法是绝对安全的。
教学意义
对于初学 OI 的同学,如果一时想不通
((pos + k) % n + n) % n这个公式,用while循环进行加减修正是一个非常棒的替代策略。它逻辑清晰,不易写错,体现了“用计算机的算力换取人脑的逻辑简化”这一思想。 - 用一个变量
-
0
这是一个用于生成“环状序列游走”题目测试数据的完整 C++ 代码。
它包含了一个**标准解答(Solution)用来生成正确的
.out文件,以及一个数据生成器(Generator)**用来构造覆盖各种边缘情况的.in文件。数据点设计策略
为了确保测试数据的有效性,设计了以下 10 个测试点:
- #1 (样例):题目中给出的样例,用于基础验证。
- #2 (纯顺时针): 全部为正数,测试基础取模。
- #3 (纯逆时针): 全部为负数,测试负数取模逻辑
(idx + k) % n + n) % n。 - #4 (原地不动):,无论怎么走都应该停在下标 0。这是极端的边界情况。
- #5 (大步流星):,步数超过数组长度,测试是否能正确处理多圈回绕。
- #6 (左右横跳): 正负交替,测试累加逻辑的稳定性。
- #7 (零步测试):包含 的情况(虽然题目暗示 ,但程序应能处理原地不动)。
- #8 (最大数据 1):,常规随机。
- #9 (最大数据 2):,数值较大。
- #10 (压力混合):各种情况的混合,确保没有逻辑漏洞。
Generator 代码 (gen.cpp)
你可以直接保存为
gen.cpp,编译运行即可生成1.in/1.out到10.in/10.out。#include <bits/stdc++.h> using namespace std; // ========================================== // Part 1: 标准解答 (Standard Solution) // 用于根据 input 生成正确的 output // ========================================== int solve(int n, int m, const vector<int>& a, const vector<int>& steps) { int current_pos = 0; // 初始位置 0 for (int k : steps) { // 核心公式:处理正负数移动及多圈回绕 // 1. (current_pos + k) 可能为负 // 2. % n 后可能仍为负 (取决于语言特性,C++中负数取模仍为负) // 3. + n 保证为正 // 4. 再 % n 处理正溢出 current_pos = ((current_pos + k) % n + n) % n; } return a[current_pos]; } // ========================================== // Part 2: 数据生成器工具 (Generator Tools) // ========================================== mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); // 生成 [L, R] 范围内的随机整数 int random_int(int L, int R) { return uniform_int_distribution<int>(L, R)(rng); } // 生成指定模式的数据 void generate_case(int case_id) { int n, m; vector<int> a; vector<int> steps; // 根据测试点 ID 构造不同特性的数据 switch (case_id) { case 1: // 题目样例 n = 5; m = 4; a = {10, 20, 30, 40, 50}; steps = {2, -1, 4, -2}; break; case 2: // 纯顺时针 (k > 0) n = 10; m = 5; for(int i=0; i<n; i++) a.push_back(random_int(1, 100)); for(int i=0; i<m; i++) steps.push_back(random_int(1, 5)); break; case 3: // 纯逆时针 (k < 0) n = 10; m = 5; for(int i=0; i<n; i++) a.push_back(random_int(1, 100)); for(int i=0; i<m; i++) steps.push_back(random_int(-5, -1)); break; case 4: // 边界情况:只有 1 个元素 n = 1; m = 20; a.push_back(999); for(int i=0; i<m; i++) steps.push_back(random_int(-10, 10)); break; case 5: // 步数大于长度 (|k| > N) n = 5; m = 10; for(int i=0; i<n; i++) a.push_back(i * 10); for(int i=0; i<m; i++) { // 生成绝对值比 n 大的步数 int k = random_int(6, 20); if (random_int(0, 1)) k = -k; steps.push_back(k); } break; case 6: // 左右横跳 (正负交替) n = 20; m = 50; for(int i=0; i<n; i++) a.push_back(random_int(1, 1000)); for(int i=0; i<m; i++) { int k = random_int(1, 10); if (i % 2 == 1) k = -k; steps.push_back(k); } break; case 7: // 包含原地不动 (k = 0) n = 50; m = 50; for(int i=0; i<n; i++) a.push_back(random_int(1, 1000)); for(int i=0; i<m; i++) steps.push_back(random_int(-5, 5)); break; case 8: // 满数据范围 (N=100, M=100) - 随机 n = 100; m = 100; for(int i=0; i<n; i++) a.push_back(random_int(1, 1000)); for(int i=0; i<m; i++) steps.push_back(random_int(-100, 100)); break; case 9: // 满数据范围 - 大步数 n = 100; m = 100; for(int i=0; i<n; i++) a.push_back(random_int(1, 1000)); for(int i=0; i<m; i++) { // 生成接近 100 的步数 steps.push_back(random_int(-100, 100)); } break; case 10: // 混合压力测试 n = random_int(80, 100); m = random_int(80, 100); for(int i=0; i<n; i++) a.push_back(random_int(1, 1000)); for(int i=0; i<m; i++) steps.push_back(random_int(-100, 100)); break; default: break; } // ------------------------------------ // 生成文件逻辑 // ------------------------------------ string in_file = to_string(case_id) + ".in"; string out_file = to_string(case_id) + ".out"; // 1. 写入 .in 文件 ofstream fin(in_file); fin << n << " " << m << "\n"; for(int i=0; i<n; i++) fin << a[i] << (i==n-1 ? "" : " "); fin << "\n"; for(int i=0; i<m; i++) fin << steps[i] << (i==m-1 ? "" : " "); fin << "\n"; fin.close(); // 2. 计算答案并写入 .out 文件 int ans = solve(n, m, a, steps); ofstream fout(out_file); fout << ans << "\n"; fout.close(); cout << "Generated Case " << case_id << ": N=" << n << ", M=" << m << endl; } int main() { // 循环生成 1-10 号测试点 for (int i = 1; i <= 10; ++i) { generate_case(i); } cout << "All test cases generated successfully!" << endl; return 0; }如何使用
- 编译:
在终端或命令行中输入:
g++ gen.cpp -o gen -std=c++14 - 运行:
./gen # Windows 下是 gen.exe - 结果:
当前目录下会生成
1.in,1.out...10.in,10.out共 20 个文件。.in文件包含输入数据。.out文件包含对应的正确答案。
检查建议
你可以打开
4.in(单元素数组测试),确认内容如下:1 20 999 ... (20个随机指令)并检查
4.out,因为数组只有一个元素,无论怎么走,最终结果必然是999。如果你的标准代码输出了其他值,说明取模逻辑有 bug(例如对 1 取模出错了)。这个生成器里的solve函数已经使用了最稳健的写法((pos + k) % n + n) % n,确保数据是正确的。 -
0
【解题提示】
这道题唯一的坑点在于
$$\text{index} = ( (\text{current} + \text{step}) \% N + N ) \% N $$k为负数时。 在 C++ 中,-2 % 5的结果是-2,而不是我们期望的循环下标3。 为了解决这个问题,我们需要使用课件中提到的“万能取模公式”(用于确保mod运算结果不为负的技巧:把mod的结果加上1倍模数再取模):这个公式无论是正向越界还是反向越界,都能计算出正确的下标。
【参考代码 (C++14)】
#include <iostream> #include <vector> using namespace std; int main() { // 1. 基础 I/O 设置 int n, m; cin >> n >> m; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } // 2. 模拟游走 int current_pos = 0; // 初始位置 for (int i = 0; i < m; i++) { int k; cin >> k; // 核心公式:处理正负数移动 // 写法一:直接加 k,然后用通用公式修正 // % n 可能得到负数,所以要再 + n 再 % n current_pos = ((current_pos + k) % n + n) % n; // 写法二(分情况讨论,适合初学者理解): // if (k > 0) current_pos = (current_pos + k) % n; // else current_pos = (current_pos + k + n * 100) % n; // 加上足够的 n 保证为正 } // 3. 输出结果 cout << a[current_pos] << endl; return 0; }
- 1
信息
- ID
- 19263
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者