1 条题解

  • 0
    @ 2025-12-16 10:37:17

    好的,这是一份完整的、可以直接运行的 C++ 数据生成器。

    它会将标准解法嵌入其中,一次性生成 1.in/1.out10.in/10.out。针对题目要求的 N105N \le 10^5 规模,我设计了覆盖 极小值、全0/全1边界、交替特例、大规模随机、大规模构造 等各种情况的测试点。

    为了保证生成器运行效率,内置的标准解答使用了 数组代替哈希表(Array with Offset) 的优化方案,速度极快,不会超时。

    数据生成器代码 (C++14)

    #include <iostream>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <random>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    // ==========================================
    // 部分 1: 高效的标准解答 (Solver)
    // 用于生成 .out 文件
    // 使用数组模拟哈希表,时间复杂度 O(N),常数极小
    // ==========================================
    int solve(const vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return 0;
    
        // 前缀和范围是 [-n, n],总共 2*n + 1 种可能
        // 使用 vector 作为 lookup table,比 unordered_map 快
        // 初始化为 -2 表示未访问过 (-1 是我们要用的有效下标)
        vector<int> mp(2 * n + 1, -2);
        
        int offset = n; // 偏移量,将前缀和 [-n, n] 映射到下标 [0, 2n]
        mp[0 + offset] = -1; // 初始状态:和为0在下标-1处出现
    
        int current_sum = 0;
        int max_len = 0;
    
        for (int i = 0; i < n; ++i) {
            // 0 -> -1, 1 -> 1
            current_sum += (nums[i] == 0 ? -1 : 1);
            
            int idx = current_sum + offset;
            
            if (mp[idx] != -2) {
                // 之前出现过该前缀和
                max_len = max(max_len, i - mp[idx]);
            } else {
                // 第一次出现,记录下标
                mp[idx] = i;
            }
        }
        return max_len;
    }
    
    // ==========================================
    // 部分 2: 数据生成逻辑
    // ==========================================
    int main() {
        // 随机数生成器初始化
        random_device rd;
        mt19937 gen(rd());
        
        // 定义10个测试点的配置
        const int TEST_CASE_COUNT = 10;
        
        for (int case_id = 1; case_id <= TEST_CASE_COUNT; ++case_id) {
            string in_file = to_string(case_id) + ".in";
            string out_file = to_string(case_id) + ".out";
            
            ofstream fout_in(in_file);
            ofstream fout_out(out_file);
            
            if (!fout_in.is_open() || !fout_out.is_open()) {
                cerr << "Error opening file for case " << case_id << endl;
                continue;
            }
    
            int N;
            vector<int> nums;
    
            // --- 针对不同测试点设计数据 ---
            
            if (case_id == 1) {
                // 测试点 1: 极小数据 (N=1)
                // 边界情况:只有一个元素,不可能平衡
                N = 1;
                nums = {0};
            }
            else if (case_id == 2) {
                // 测试点 2: 小数据 (N=10)
                // 手动构造:简单交替
                N = 10;
                nums = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1}; // 答案应为 10
            }
            else if (case_id == 3) {
                // 测试点 3: 边界情况 (N=100)
                // 全是 0,答案应为 0
                N = 100;
                nums.assign(N, 0);
            }
            else if (case_id == 4) {
                // 测试点 4: 边界情况 (N=100)
                // 全是 1,答案应为 0
                N = 100;
                nums.assign(N, 1);
            }
            else if (case_id == 5) {
                // 测试点 5: 特殊构造 (N=1000)
                // 前半部分全是0,后半部分全是1
                // 答案应该是中间那一段 [0, 1] 长度为2 (除非正好中心对齐)
                // 让我们构造得稍微复杂点:00...0011...11
                N = 1000;
                for(int i=0; i<N/2; ++i) nums.push_back(0);
                for(int i=0; i<N/2; ++i) nums.push_back(1);
                // 这种结构答案通常是 2 * min(0的个数, 1的个数),这里是 1000
            }
            else if (case_id == 6) {
                // 测试点 6: 特殊构造 (N=2000)
                // 稀疏的 1。绝大多数是 0,只有极少数 1。
                N = 2000;
                nums.assign(N, 0);
                uniform_int_distribution<> dis(0, N-1);
                for(int k=0; k<20; ++k) nums[dis(gen)] = 1;
            }
            else if (case_id == 7) {
                // 测试点 7: 中等规模随机 (N=10000)
                N = 10000;
                uniform_int_distribution<> dis(0, 1);
                for(int i=0; i<N; ++i) nums.push_back(dis(gen));
            }
            else if (case_id == 8) {
                // 测试点 8: 大规模完全随机 (N=100000)
                // 压力测试:普通随机
                N = 100000;
                uniform_int_distribution<> dis(0, 1);
                for(int i=0; i<N; ++i) nums.push_back(dis(gen));
            }
            else if (case_id == 9) {
                // 测试点 9: 大规模最坏情况 (N=100000)
                // 构造长距离:开头一个 0,结尾一个 1,中间全是 1
                // 或者 0 1 0 1 ... 答案是 N
                N = 100000;
                for(int i=0; i<N; ++i) nums.push_back(i % 2);
            }
            else if (case_id == 10) {
                // 测试点 10: 大规模构造 (N=100000)
                // 包含大量随机波动,模拟真实复杂场景
                N = 100000;
                // 先生成一半 0 一半 1,然后 shuffle,保证答案大概率很大但不一定是N
                for(int i=0; i<N/2; ++i) nums.push_back(0);
                for(int i=0; i<N - N/2; ++i) nums.push_back(1);
                shuffle(nums.begin(), nums.end(), gen);
            }
    
            // --- 写入 .in 文件 ---
            fout_in << N << "\n";
            for (int i = 0; i < N; ++i) {
                fout_in << nums[i] << (i == N - 1 ? "" : " ");
            }
            fout_in << "\n";
            fout_in.close();
    
            // --- 计算并写入 .out 文件 ---
            int ans = solve(nums);
            fout_out << ans << "\n";
            fout_out.close();
            
            cout << "Generated Case " << case_id << ": N=" << N << ", Answer=" << ans << endl;
        }
    
        return 0;
    }
    

    生成数据策略说明 (覆盖率分析)

    测试点 NN 大小 数据特征 目的
    1 1 只有 1 个数字 [0] 边界测试:极小值,验证程序不崩溃且输出0。
    2 10 0 1 0 1 ... 基础测试:简单的交替序列,验证基本逻辑。
    3 100 全是 0 边界测试:无 1 的情况,答案应为 0。
    4 全是 1 边界测试:无 0 的情况,答案应为 0。
    5 1000 00...0011...11 逻辑测试:前一半0后一半1,类似山峰,测试前缀和升降逻辑。
    6 2000 大量 0,极少量 1 稀疏测试:不平衡数据,答案通常很小。
    7 10000 随机 性能测试:中等规模随机数据。
    8 10510^5 压力测试:最大规模随机数据,验证 O(N)O(N) 复杂度。
    9 0 1 0 1 ... 极限测试:最大规模且答案为 NN (满长度)。
    10 50% 0, 50% 1 (Shuffle) 综合测试:通过洗牌保证 0 和 1 数量相等,答案通常非常接近 NN,测试 Hash/Array 碰撞处理能力。

    如何使用

    1. 将代码保存为 generator.cpp
    2. 编译:g++ generator.cpp -o generator -O2
    3. 运行:./generator
    4. 运行结束后,目录下会生成 1.in ~ 10.in1.out ~ 10.out 共 20 个文件。

    代码中的优化点

    1. Array 代替 Map: 在 solve 函数中,我使用了 vector<int> mp(2 * n + 1, -2) 配合 offset
      • std::unordered_map 虽然平均 O(1)O(1),但在 worst case 或发生大量碰撞时会变慢,且常数较大。
      • Array 访问是严格的 O(1)O(1),内存访问连续性更好,对于 N=105N=10^5 的数据生成非常快。
    2. I/O: 使用 ofstream 直接写文件,格式符合 OI 题目标准(空格分隔数组元素)。
    3. 随机性: 使用了 std::mt19937 (Mersenne Twister),比 rand() 质量更高,生成的测试数据分布更均匀。
    • 1

    信息

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