4 条题解

  • 0
    @ 2026-1-25 9:23:10

    这是一个完整的 C++ 数据生成器。它不仅生成 .in 输入文件,还会利用 STL 的 unordered_map 作为“标准答案”生成对应的 .out 输出文件。

    该生成器包含 10 个测试点,设计逻辑覆盖了:小数据、大数据压测、大量哈希冲突(针对模数攻击)、频繁增删改、扩容触发测试以及边界值测试。

    使用说明

    1. 将下方代码保存为 gen.cpp
    2. 编译:g++ gen.cpp -o gen -O2 -std=c++14
    3. 运行:./gen
    4. 运行结束后,当前目录下会生成 1.in/1.out10.in/10.out

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

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <string>
    #include <random>
    #include <unordered_map>
    #include <algorithm>
    #include <chrono>
    
    using namespace std;
    
    // 配置参数
    const int TOTAL_POINTS = 10;
    const int MAX_KEY = 1000000000;
    
    // 随机数生成器初始化
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    
    // 生成指定范围内的随机整数 [l, r]
    int rand_int(int l, int r) {
        uniform_int_distribution<int> dist(l, r);
        return dist(rng);
    }
    
    // ---------------------------------------------------------
    // 标准答案模拟器 (用于生成 .out 文件)
    // 使用 std::unordered_map 保证逻辑正确性
    // ---------------------------------------------------------
    class StdSolver {
        unordered_map<int, int> mp;
    public:
        void put(int k, int v) {
            mp[k] = v;
        }
        
        int get(int k) {
            if (mp.find(k) != mp.end()) {
                return mp[k];
            }
            return -1;
        }
        
        void remove(int k) {
            mp.erase(k);
        }
        
        void clear() {
            mp.clear();
        }
    };
    
    // ---------------------------------------------------------
    // 测试点生成逻辑
    // ---------------------------------------------------------
    void generate_point(int point_id) {
        string in_file = to_string(point_id) + ".in";
        string out_file = to_string(point_id) + ".out";
        
        ofstream fin(in_file);
        ofstream fout(out_file);
        
        StdSolver solver;
        vector<int> known_keys; // 用于记录已知存在的key,方便生成有效的get/remove操作
        
        int N; // 操作数量
        
        // 针对不同测试点的策略配置
        if (point_id <= 2) {
            // Points 1-2: 小规模数据,基础功能测试
            N = 100;
            int key_range = 50;
            
            fin << N << endl;
            for (int i = 0; i < N; ++i) {
                int op = rand_int(0, 2); // 0: put, 1: get, 2: remove
                if (op == 0) {
                    int k = rand_int(1, key_range);
                    int v = rand_int(1, 1000);
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                    known_keys.push_back(k);
                } else if (op == 1) {
                    // 50% 概率查存在的,50% 概率查随机的
                    int k = (known_keys.empty() || rand_int(0, 1)) ? rand_int(1, key_range) : known_keys[rand_int(0, known_keys.size()-1)];
                    fin << "get " << k << endl;
                    fout << solver.get(k) << endl;
                } else {
                    int k = (known_keys.empty() || rand_int(0, 1)) ? rand_int(1, key_range) : known_keys[rand_int(0, known_keys.size()-1)];
                    fin << "remove " << k << endl;
                    solver.remove(k);
                }
            }
        } 
        else if (point_id <= 4) {
            // Points 3-4: 纯插入与查询,主要测试扩容后的查找
            N = 100000;
            fin << N << endl;
            
            // 前 70% 是插入,后 30% 是查询
            for (int i = 0; i < N; ++i) {
                if (i < N * 0.7) {
                    int k = rand_int(1, MAX_KEY);
                    int v = rand_int(1, MAX_KEY);
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                    known_keys.push_back(k);
                } else {
                    int k = (known_keys.empty() || rand_int(0, 3)) ? rand_int(1, MAX_KEY) : known_keys[rand_int(0, known_keys.size()-1)];
                    fin << "get " << k << endl;
                    fout << solver.get(k) << endl;
                }
            }
        }
        else if (point_id <= 6) {
            // Points 5-6: 频繁更新与删除,测试内存复用和链表删除逻辑
            N = 100000;
            fin << N << endl;
            int key_range = 5000; // 较小的 Key 范围意味着高重复率(Update)
            
            for (int i = 0; i < N; ++i) {
                int op = rand_int(0, 9);
                // 40% put, 30% get, 30% remove
                if (op < 4) { 
                    int k = rand_int(1, key_range);
                    int v = rand_int(1, MAX_KEY);
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                    known_keys.push_back(k);
                } else if (op < 7) {
                    int k = rand_int(1, key_range);
                    fin << "get " << k << endl;
                    fout << solver.get(k) << endl;
                } else {
                    int k = rand_int(1, key_range);
                    fin << "remove " << k << endl;
                    solver.remove(k);
                }
            }
        }
        else if (point_id == 7) {
            // Point 7: 扩容触发测试 (Rehash Stress Test)
            // 持续插入不同的 Key,强制哈希表从 16 -> 32 -> ... -> 大容量
            N = 100000;
            fin << N << endl;
            
            for(int i = 0; i < N; ++i) {
                // 每隔 100 次做一次查询,其余全插入
                if (i % 100 == 0 && !known_keys.empty()) {
                     int k = known_keys[rand_int(0, known_keys.size()-1)];
                     fin << "get " << k << endl;
                     fout << solver.get(k) << endl;
                } else {
                    int k = i; // 连续的 key
                    int v = i * 10;
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                    known_keys.push_back(k);
                }
            }
        }
        else if (point_id == 8) {
            // Point 8: 哈希冲突攻击 (Collision Test)
            // 构造容易冲突的数据。题目初始容量 16,扩容通常是 2 的幂次。
            // 生成大量 2^k 倍数的 key,使它们在 key % capacity 时容易撞车。
            N = 100000;
            fin << N << endl;
            
            int step = 256; // 2^8, 对低位取模容易冲突
            for(int i = 0; i < N; ++i) {
                int op = rand_int(0, 2);
                if (op == 0) {
                    int k = (i + 1) * step; // 256, 512, 768...
                    int v = i;
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                    known_keys.push_back(k);
                } else if (op == 1) {
                    // 查询操作
                    int k = (known_keys.empty()) ? step : known_keys[rand_int(0, known_keys.size()-1)];
                    fin << "get " << k << endl;
                    fout << solver.get(k) << endl;
                } else {
                    // 删除操作
                    int k = (known_keys.empty()) ? step : known_keys[rand_int(0, known_keys.size()-1)];
                    fin << "remove " << k << endl;
                    solver.remove(k);
                }
            }
        }
        else if (point_id == 9) {
            // Point 9: 边界情况 (Edge Cases)
            // Key = 0, Key = MAX_INT, 重复删除不存在的 Key
            N = 50000;
            fin << N << endl;
            
            vector<int> special_keys = {0, 1, 2, MAX_KEY, MAX_KEY - 1};
            
            for(int i = 0; i < N; ++i) {
                int op = rand_int(0, 2);
                int k = special_keys[rand_int(0, special_keys.size() - 1)];
                
                if (op == 0) {
                    int v = rand_int(0, 100);
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                } else if (op == 1) {
                    fin << "get " << k << endl;
                    fout << solver.get(k) << endl;
                } else {
                    fin << "remove " << k << endl;
                    solver.remove(k);
                }
            }
        }
        else {
            // Point 10: 综合随机大数据 (Mixed Large)
            N = 100000;
            fin << N << endl;
            
            for(int i = 0; i < N; ++i) {
                int op = rand_int(0, 2);
                int k = (op == 0 || known_keys.empty() || rand_int(0, 3) == 0) 
                        ? rand_int(0, MAX_KEY) 
                        : known_keys[rand_int(0, known_keys.size()-1)];
                
                if (op == 0) {
                    int v = rand_int(0, MAX_KEY);
                    fin << "put " << k << " " << v << endl;
                    solver.put(k, v);
                    known_keys.push_back(k);
                } else if (op == 1) {
                    fin << "get " << k << endl;
                    fout << solver.get(k) << endl;
                } else {
                    fin << "remove " << k << endl;
                    solver.remove(k);
                }
            }
        }
    
        fin.close();
        fout.close();
        cout << "Test point " << point_id << " generated." << endl;
    }
    
    int main() {
        // 提升 IO 效率,虽然对生成器不是必须的
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
    
        cout << "Generating " << TOTAL_POINTS << " test cases..." << endl;
        for (int i = 1; i <= TOTAL_POINTS; ++i) {
            generate_point(i);
        }
        cout << "Done! Files 1.in/out through 10.in/out created." << endl;
        
        return 0;
    }
    

    数据点设计策略详解

    测试点 ID 数据规模 N 说明 考察点
    1-2 100 小范围 Key,少量操作 算法正确性,基本逻辑
    3-4 100,000 大范围 Key,主要是 putget 大数据量下的基本哈希性能
    5-6 Key 范围小 (5000),高频重复 哈希冲突处理 (拉链法)、值更新、链表节点查找
    7 连续 Key,只增不减 扩容 (Rehash) 逻辑是否正确,是否因扩容导致超时
    8 Key 为 256 的倍数 哈希攻击,测试哈希函数是否均匀,或链表过长时的性能
    9 50,000 包含 0, INT_MAX, 不存在的 Key 边界条件,删除不存在的元素,查询空元素
    10 100,000 纯随机大数据 综合稳定性测试

    注意事项

    1. 文件大小:输入文件中全是整数和简单的指令字符串。对于 N=105N=10^5,每行平均约 15 字节,总大小约 1.5MB,符合 2MB 的限制。
    2. 效率:使用了 std::unordered_map 作为参考答案,其本身效率极高,生成 10 组数据在现代 CPU 上通常在 1 秒内完成。
    3. 安全性:代码中避免了除以 0,且使用了 vector 记录已生成的 key 来保证 getremove 操作有较高的命中率(避免全部输出 -1 导致测试无效)。

    信息

    ID
    19472
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    (无)
    递交数
    5
    已通过
    2
    上传者