2 条题解
-
0
你好!我是金牌教练。为了帮你高效、专业地创建 OJ(在线判题系统)测试数据,我为你编写了这个集成化的数据生成器与标程(Generator & Solver)。
虽然 LeetCode 原题的 规模对于 暴力算法来说非常轻松,但在 NOI 竞赛中,我们通常会通过构造特定的边界数据来检验选手的代码健壮性。
一、 测试点设计矩阵
测试点 规模 目标位置/特点 考核重点 1-2 10 随机小数据 基础逻辑正确性 3 10000 目标在下标 0 头部边界 4 目标在下标 尾部边界 5 目标比数组最小值还小 左侧越界判定 6 目标比数组最大值还大 右侧越界判定 7 目标在两个数组元素之间(不存在) 中间断档判定 8 1 数组仅一个元素且命中 极小规模边界 9 数组仅一个元素且未命中 10 10000 极限规模随机不重复序列 综合性能与健壮性
二、 数据生成器 C++ 代码
该程序采用 C++14 标准,逻辑清晰,不含递归,并使用了
mt19937随机引擎确保数据分布的科学性。#include <iostream> #include <fstream> #include <vector> #include <string> #include <random> #include <algorithm> #include <set> #include <ctime> using namespace std; // --- 标程 (最优复杂度 O(log N)) --- int solve(int n, int target, const vector<int>& nums) { int left = 0, right = n - 1; while (left <= right) { int mid = left + (right - left) / 2; // 防溢出 if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1; } // --- 数据生成逻辑 --- void make_data() { mt19937 rng(time(0)); uniform_int_distribution<int> dist(-9999, 9999); for (int t = 1; t <= 10; t++) { string in_name = to_string(t) + ".in"; string out_name = to_string(t) + ".out"; ofstream fout(in_name); ofstream fans(out_name); int n, target; vector<int> nums; set<int> unique_nums; // 根据测试点矩阵设计规模 if (t <= 2) n = 10; else if (t >= 8 && t <= 9) n = 1; else n = 10000; // 构造不重复有序序列 if (t == 5) { // 保证 target 极小 target = -9999; while (unique_nums.size() < (size_t)n) unique_nums.insert(dist(rng) + 100); } else if (t == 6) { // 保证 target 极大 target = 9999; while (unique_nums.size() < (size_t)n) unique_nums.insert(dist(rng) - 100); } else if (t == 7) { // 保证 target 在缝隙 target = 500; while (unique_nums.size() < (size_t)n) { int val = dist(rng); if (val != 500) unique_nums.insert(val); } } else { while (unique_nums.size() < (size_t)n) unique_nums.insert(dist(rng)); } for (int x : unique_nums) nums.push_back(x); sort(nums.begin(), nums.end()); // 设定特定位置的 target if (t == 3) target = nums[0]; else if (t == 4) target = nums[n - 1]; else if (t == 8) target = nums[0]; else if (t == 9) target = nums[0] + 1; else if (t <= 2 || t == 10) { if (rng() % 2 == 0) target = nums[rng() % n]; // 50% 概率命中 else target = dist(rng); } // 1. 输出 .in 文件 fout << n << " " << target << "\n"; for (int i = 0; i < n; i++) { fout << nums[i] << (i == n - 1 ? "" : " "); } fout << endl; // 2. 调用标程生成 .out 文件 int result = solve(n, target, nums); fans << result << endl; cout << "Generated Case " << t << " successfully." << endl; fout.close(); fans.close(); } } int main() { make_data(); return 0; }
三、 生成器关键细节分析
-
文件大小控制:
- 本题数据量 ,数组中每个数字加上空格平均按 6 字节计算,
.in文件大小约为 60KB。 - 10 个测试点总共约 0.6MB,远小于 2MB 的要求,非常方便在 OJ 系统上传。
- 本题数据量 ,数组中每个数字加上空格平均按 6 字节计算,
-
满足“不重复”约束:
- 使用了
std::set自动去重并排序,确保生成的nums严格满足题目“不重复”且“升序”的描述。
- 使用了
-
计算稳定性:
- 防溢出:标程中使用
mid = left + (right - left) / 2,这是在 NOI 比赛中防止left + right超过int上界的标准做法。 - 非递归逻辑:数据生成器和标程均采用迭代写法,彻底杜绝了在大规模数据下的爆栈风险。
- 防溢出:标程中使用
-
区分算法复杂度:
- 虽然对于 规模暴力解法通常能通过,但对于第 5、6、7 个测试点,如果选手在代码中没有正确处理
target超出数组边界或处于元素间隙的情况,线性扫描或者写得不精细的二分都会出错。 - 优化提示:如果你需要完全卡死 暴力,只需将生成器中的 改为 。
- 虽然对于 规模暴力解法通常能通过,但对于第 5、6、7 个测试点,如果选手在代码中没有正确处理
四、 如何使用
- 将上述代码复制到 C++ 编辑器。
- 编译并运行。
- 程序会在当前目录下自动生成
1.in,1.out到10.out。 - 将这些文件打包成一个
.zip文件,即可上传到你的 OJ 题目后台。
-
信息
- ID
- 19499
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者