2 条题解

  • 0
    @ 2026-3-26 10:51:52

    你好!我是金牌教练。为了帮你高效、专业地创建 OJ(在线判题系统)测试数据,我为你编写了这个集成化的数据生成器与标程(Generator & Solver)

    虽然 LeetCode 原题的 N=104N=10^4 规模对于 O(N)O(N) 暴力算法来说非常轻松,但在 NOI 竞赛中,我们通常会通过构造特定的边界数据来检验选手的代码健壮性。

    一、 测试点设计矩阵

    测试点 NN 规模 目标位置/特点 考核重点
    1-2 10 随机小数据 基础逻辑正确性
    3 10000 目标在下标 0 头部边界
    4 目标在下标 N1N-1 尾部边界
    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;
    }
    

    三、 生成器关键细节分析

    1. 文件大小控制

      • 本题数据量 N=104N=10^4,数组中每个数字加上空格平均按 6 字节计算,.in 文件大小约为 60KB。
      • 10 个测试点总共约 0.6MB,远小于 2MB 的要求,非常方便在 OJ 系统上传。
    2. 满足“不重复”约束

      • 使用了 std::set 自动去重并排序,确保生成的 nums 严格满足题目“不重复”且“升序”的描述。
    3. 计算稳定性

      • 防溢出:标程中使用 mid = left + (right - left) / 2,这是在 NOI 比赛中防止 left + right 超过 int 上界的标准做法。
      • 非递归逻辑:数据生成器和标程均采用迭代写法,彻底杜绝了在大规模数据下的爆栈风险。
    4. 区分算法复杂度

      • 虽然对于 10410^4 规模暴力解法通常能通过,但对于第 5、6、7 个测试点,如果选手在代码中没有正确处理 target 超出数组边界或处于元素间隙的情况,线性扫描或者写得不精细的二分都会出错。
      • 优化提示:如果你需要完全卡死 O(N)O(N) 暴力,只需将生成器中的 NN 改为 10610^6

    四、 如何使用

    1. 将上述代码复制到 C++ 编辑器。
    2. 编译并运行。
    3. 程序会在当前目录下自动生成 1.in, 1.out10.out
    4. 将这些文件打包成一个 .zip 文件,即可上传到你的 OJ 题目后台。

    信息

    ID
    19499
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者