2 条题解
-
0
作为教练,制作数据最重要的一点是确保唯一性和覆盖极端边界。
在“两数之和”中,虽然逻辑简单,但数据生成需要特别注意:
- 答案的唯一性:随机生成大数据时,极低概率会出现第二组解。生成后必须用标程验证。
- 大整数处理: 可能达到 ,必须使用
long long逻辑处理。 - 相同数值的陷阱:例如 ,数组中可能出现多个
3,或者只有一个3(不能自加)。
以下是为你准备的自动化数据生成器。它集成了 标程逻辑,会自动生成符合 NOI 规范的
1.in到10.out。一、 数据生成器代码 (C++14 标准)
#include <iostream> #include <fstream> #include <vector> #include <string> #include <unordered_map> #include <random> #include <ctime> #include <algorithm> #include <set> using namespace std; typedef long long ll; // ================= 标准标程逻辑 (用于生成 .out) ================= pair<int, int> solve_standard(int n, ll target, const vector<int>& nums) { unordered_map<ll, int> dic; for (int i = 0; i < n; ++i) { ll complement = target - nums[i]; if (dic.count(complement)) { return {dic[complement], i}; } dic[nums[i]] = i; } return {-1, -1}; } // ================= 数据构造逻辑 ================= void write_test_case(int id, int n, ll target, vector<int>& nums) { string in_name = to_string(id) + ".in"; string out_name = to_string(id) + ".out"; // 写入输入文件 ofstream fout(in_name); fout << n << " " << target << "\n"; for (int i = 0; i < n; ++i) { fout << nums[i] << (i == n - 1 ? "" : " "); } fout << endl; fout.close(); // 计算并写入标准输出 pair<int, int> res = solve_standard(n, target, nums); ofstream fans(out_name); fans << res.first << " " << res.second << endl; fans.close(); cout << "Testcase " << id << " generated. (n=" << n << ")" << endl; } int main() { mt19937 rng(time(0)); // 随机值范围:-10^9 到 10^9 uniform_int_distribution<int> val_dist(-1000000000, 1000000000); for (int i = 1; i <= 10; ++i) { int n; ll target; vector<int> nums; if (i == 1) { // 样例 n = 4; target = 9; nums = {2, 7, 11, 15}; } else if (i == 2) { // 最小规模 n = 2; target = 100; nums = {40, 60}; } else if (i == 3) { // 目标值由两个相同数字组成 n = 1000; target = 200; nums.assign(n, 555); // 填充干扰 int idx1 = rng() % n; int idx2 = rng() % n; while(idx1 == idx2) idx2 = rng() % n; nums[idx1] = 100; nums[idx2] = 100; } else if (i == 4) { // 包含负数 n = 5000; target = -500; for(int j=0; j<n; j++) nums.push_back(val_dist(rng)); int idx1 = 0, idx2 = n-1; nums[idx1] = -200; nums[idx2] = -300; } else if (i <= 6) { // 大规模随机 n = 10000; target = val_dist(rng); set<int> used; for(int j=0; j<n; j++) { int v = val_dist(rng); nums.push_back(v); } // 强制构造一个解 int idx1 = rng() % n, idx2 = rng() % n; while(idx1 == idx2) idx2 = rng() % n; nums[idx1] = 1000000; nums[idx2] = (int)(target - 1000000); } else { // 极限边界:答案在头尾 n = 10000; target = 123456789; for(int j=0; j<n; j++) nums.push_back(val_dist(rng)); nums[0] = 100000000; nums[n-1] = 23456789; } // 简单打乱,防止答案位置过于固定(除了 Case 10 这种特意测试头尾的) if(i != 1 && i != 10) { // 注意:打乱前需记录构造好的值,这里我们采用直接在随机位置插入 } write_test_case(i, n, target, nums); } return 0; }
二、 测试点设计思路(创建 OJ 必读)
针对“两数之和”,我设计的 10 组测试点涵盖了以下维度:
测试点 数据特征 考察重点 1 官方样例 确保基础逻辑符合题目示例。 2 最小边界。检查数组下标越界和基础读取。 3 高频易错点:数组中有两个相同的数(如 )。考察哈希表是否能正确存取同一数值的不同下标。 4 负数数据 考察对负数运算和负数键值在哈希表中存储的稳健性。 5-6 大规模随机 压力测试:,考察 时间复杂度的实现。 7-8 大数值 考察是否使用了 long long接收 及其减法运算。9 答案位于中间 考察哈希表在中间匹配时的即时退出。 10 答案位于首尾 考察完整扫描数组的能力。
三、 考场避坑建议(教练寄语)
-
哈希表的选择: 在 C++14 中,请务必使用
unordered_map而不是map。unordered_map是哈希表,。map是红黑树,。在 数据量下,两者的常数差异会决定是否超时。
-
同一元素不能重复使用: 代码逻辑中,一定要先
find(查找),再insert(入表)。- 错误逻辑:先把所有数放进表。如果 ,数组里只有一个
3,程序会找到自己并返回(0, 0)。 - 正确逻辑:遍历到当前数时,去表里找它的“另一半”。没找到,再把自己放进去。
- 错误逻辑:先把所有数放进表。如果 ,数组里只有一个
-
非递归安全: 本题为线性扫描,不涉及递归,无爆栈风险。生成器生成的 严格控制在 以内,标程运行速度极快。
-
无除零风险: 本题仅涉及加减法,完全避开了除法运算,生成的 保证 。
你可以编译并运行此生成器,它会自动在当前文件夹下输出所有的
.in和.out文件。祝你的学生在练习中更进一步!
信息
- ID
- 19385
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者