1 条题解

  • 0
    @ 2025-12-16 11:42:39

    这是一个符合OI竞赛出题标准的数据生成器(Data Generator)。它会自动生成 1.in ~ 10.in 以及对应的标准答案 1.out ~ 10.out

    设计思路

    为了保证数据的合法性(不爆 int)和覆盖性(全面测试),数据生成策略如下:

    1. 数据规模分级

      • 1-3 点:小规模数据,方便手动调试观察。
      • 4-6 点:中等规模,NN 逐渐增大,包含纯 1/-1 的情况。
      • 7-10 点:大规模数据(N=105N=10^5),重点测试性能和边界。
    2. 数值构造策略(关键):

      • 题目限制了任意前缀/后缀积都在32位整数内。这意味着数组中绝对值大于1的数非常少(因为 2312×1092^{31} \approx 2 \times 10^9,即使全是 2,最多也只能有 30 个左右)。
      • 因此,大数据生成逻辑必须是:生成少量的大数(或0),其余位置全部用 1-1 填充,最后打乱顺序
      • 0 的处理:专门构造含 1 个 0、2 个 0 和无 0 的情况。
    3. 文件生成:使用 C++ 的 fstream 进行文件读写。

    C++ 数据生成器代码

    请将以下代码保存为 gen.cpp,编译运行即可在当前目录下生成所有测试数据。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    // ==========================================
    // 配置区域
    // ==========================================
    const int TEST_CASE_COUNT = 10; // 测试点数量
    const int MAX_VAL_LIMIT = 30;   // 题目要求的数值范围 [-30, 30]
    const int INF_INT = 2000000000; // 安全的 int 阈值
    
    // ==========================================
    // 随机数生成器 (基于 mt19937)
    // ==========================================
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    
    int random_int(int l, int r) {
        uniform_int_distribution<int> dist(l, r);
        return dist(rng);
    }
    
    // ==========================================
    // 标准解答算法 (Solver)
    // 用于生成 .out 文件
    // ==========================================
    vector<int> solve(int n, const vector<int>& nums) {
        vector<int> ans(n);
        
        // 1. 计算前缀积
        ans[0] = 1;
        for (int i = 1; i < n; ++i) {
            ans[i] = ans[i - 1] * nums[i - 1];
        }
        
        // 2. 计算后缀积并合并
        int R = 1;
        for (int i = n - 1; i >= 0; --i) {
            ans[i] = ans[i] * R;
            R = R * nums[i];
        }
        
        return ans;
    }
    
    // ==========================================
    // 数据构造逻辑
    // ==========================================
    // 生成一组符合题目要求的数据
    // n: 数组长度
    // zero_count: 0 的个数 (-1 表示随机 0-2 个)
    // large_nums_allowed: 是否允许生成绝对值 > 1 的数
    vector<int> generate_case(int n, int zero_count = -1, bool large_nums_allowed = true) {
        vector<int> a;
        
        // 1. 确定 0 的个数
        if (zero_count == -1) {
            int type = random_int(0, 10);
            if (type < 7) zero_count = 0;      // 70% 概率无 0
            else if (type < 9) zero_count = 1; // 20% 概率 1 个 0
            else zero_count = 2;               // 10% 概率 2 个 0
        }
        
        // 添加 0
        for (int i = 0; i < zero_count && a.size() < n; ++i) {
            a.push_back(0);
        }
    
        // 2. 添加绝对值 > 1 的数 (控制总乘积不溢出)
        long long current_prod = 1;
        if (large_nums_allowed && a.size() < n) {
            // 尝试添加一些大数,直到乘积快溢出
            int attempts = 0;
            while (a.size() < n && attempts < 1000) {
                attempts++;
                // 随机选一个 [-30, 30] 之间非 0 非 1 的数
                int val = random_int(-MAX_VAL_LIMIT, MAX_VAL_LIMIT);
                if (val == 0 || val == 1 || val == -1) continue;
    
                // 检查乘积是否溢出 (留一点余量)
                if (abs(current_prod * val) < INF_INT) {
                    current_prod *= val;
                    a.push_back(val);
                } else {
                    // 如果已经很大了,就停止添加大数
                    break;
                }
            }
        }
    
        // 3. 剩余位置填充 1 或 -1
        while (a.size() < n) {
            a.push_back(random_int(0, 1) ? 1 : -1);
        }
    
        // 4. 打乱顺序
        shuffle(a.begin(), a.end(), rng);
    
        return a;
    }
    
    // ==========================================
    // 主程序:生成 1~10 测试点
    // ==========================================
    int main() {
        printf("Starting data generation...\n");
    
        for (int id = 1; id <= TEST_CASE_COUNT; ++id) {
            int n;
            vector<int> nums;
    
            // --- 针对不同测试点定制数据 ---
            if (id == 1) {
                // 测试点 1: 极小数据 (n=5),随机
                n = 5;
                nums = generate_case(n, 0, true);
            } 
            else if (id == 2) {
                // 测试点 2: 小数据 (n=10),包含 1 个 0
                n = 10;
                nums = generate_case(n, 1, true);
            }
            else if (id == 3) {
                // 测试点 3: 小数据 (n=20),包含 2 个 0
                n = 20;
                nums = generate_case(n, 2, true);
            }
            else if (id == 4) {
                // 测试点 4: 中数据 (n=100),全是 1 (单位元测试)
                n = 100;
                nums.assign(n, 1); 
            }
            else if (id == 5) {
                // 测试点 5: 中数据 (n=1000),全是 1 和 -1 (符号测试)
                n = 1000;
                nums = generate_case(n, 0, false); // 不允许大数,只允许 1/-1
            }
            else if (id == 6) {
                // 测试点 6: 大数据 (n=10^5),稀疏大数,无 0
                n = 100000;
                nums = generate_case(n, 0, true);
            }
            else if (id == 7) {
                // 测试点 7: 大数据 (n=10^5),稀疏大数,含 1 个 0
                n = 100000;
                nums = generate_case(n, 1, true);
            }
            else if (id == 8) {
                // 测试点 8: 大数据 (n=10^5),含多个 0 (测试全0输出)
                n = 100000;
                nums = generate_case(n, 5, true); // 5个0
            }
            else if (id == 9) {
                // 测试点 9: 大数据,乘积接近 INT_MAX (正边界)
                // 构造逻辑:尽可能乘到接近 20亿,然后填 1
                n = 100000;
                nums.clear();
                long long p = 1;
                while(nums.size() < n) {
                    int v = random_int(2, 5); // 选用较小的正数慢慢乘
                    if (p * v < 2000000000) {
                        p *= v;
                        nums.push_back(v);
                    } else {
                        break;
                    }
                }
                while(nums.size() < n) nums.push_back(1);
                shuffle(nums.begin(), nums.end(), rng);
            }
            else {
                // 测试点 10: 大数据,包含边界值 -30 和 30
                n = 100000;
                nums.clear();
                nums.push_back(30);
                nums.push_back(-30);
                while(nums.size() < n) nums.push_back(random_int(0, 1) ? 1 : -1);
                shuffle(nums.begin(), nums.end(), rng);
            }
    
            // --- 生成 .in 文件 ---
            string in_file_name = to_string(id) + ".in";
            ofstream fin(in_file_name);
            fin << n << "\n";
            for (int i = 0; i < n; ++i) {
                fin << nums[i] << (i == n - 1 ? "" : " ");
            }
            fin << "\n";
            fin.close();
    
            // --- 生成 .out 文件 ---
            string out_file_name = to_string(id) + ".out";
            ofstream fout(out_file_name);
            
            // 调用标准解答
            vector<int> result = solve(n, nums);
            
            for (int i = 0; i < n; ++i) {
                fout << result[i] << (i == n - 1 ? "" : " ");
            }
            fout << "\n";
            fout.close();
    
            printf("Generated Test Case %d: N=%d\n", id, n);
        }
    
        printf("All data generated successfully.\n");
        return 0;
    }
    

    数据测试点详细说明

    测试点 NN 数据特征 考察目的
    1 5 随机小整数,无 0 基础逻辑验证,方便肉眼 debug。
    2 10 包含 1 个 0 考察是否正确处理除 0 情况(结果应只有那个 0 对应的位置非 0)。
    3 20 包含 2 个 0 考察多 0 情况(结果应全为 0)。
    4 100 全是 1 考察对单位元的处理,避免过度计算。
    5 1000 全是 1-1 考察符号处理能力,无数值溢出风险。
    6 10510^5 稀疏大数,无 0 大数据性能测试。大部分是 1,只有少量大数,乘积逼近 INT_MAX。
    7 稀疏大数,含 1 个 0 大数据下的单 0 边界处理。
    8 5 个 0 大数据下的多 0 处理,验证算法是否做了无效计算(虽然 O(N)O(N) 无论多少0都一样)。
    9 乘积接近 正无穷 (2×1092 \times 10^9) 边界测试,确保 int 不溢出。
    10 包含 -3030 边界值测试,确保题目给定的值域范围被覆盖,且负数乘积正确。

    使用方法

    1. 将代码保存为 gen.cpp
    2. 使用 g++ 编译:g++ gen.cpp -o gen -O2
    3. 运行:./gen (Windows 下是 gen.exe)。
    4. 程序运行结束后,你的文件夹下会出现 1.in ~ 10.in1.out ~ 10.out
    5. 将这些文件打包 zip 即可上传至 OJ 后台。
    • 1

    信息

    ID
    19330
    时间
    1000ms
    内存
    32MiB
    难度
    10
    标签
    (无)
    递交数
    1
    已通过
    1
    上传者