1 条题解
-
0
好的,这是一份完整的、可以直接运行的 C++ 数据生成器。
它会将标准解法嵌入其中,一次性生成
1.in/1.out到10.in/10.out。针对题目要求的 规模,我设计了覆盖 极小值、全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; }生成数据策略说明 (覆盖率分析)
测试点 大小 数据特征 目的 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 压力测试:最大规模随机数据,验证 复杂度。 9 0 1 0 1 ...极限测试:最大规模且答案为 (满长度)。 10 50% 0, 50%1(Shuffle)综合测试:通过洗牌保证 0 和 1 数量相等,答案通常非常接近 ,测试 Hash/Array 碰撞处理能力。 如何使用
- 将代码保存为
generator.cpp。 - 编译:
g++ generator.cpp -o generator -O2 - 运行:
./generator - 运行结束后,目录下会生成
1.in~10.in和1.out~10.out共 20 个文件。
代码中的优化点
- Array 代替 Map: 在
solve函数中,我使用了vector<int> mp(2 * n + 1, -2)配合offset。std::unordered_map虽然平均 ,但在 worst case 或发生大量碰撞时会变慢,且常数较大。Array访问是严格的 ,内存访问连续性更好,对于 的数据生成非常快。
- I/O: 使用
ofstream直接写文件,格式符合 OI 题目标准(空格分隔数组元素)。 - 随机性: 使用了
std::mt19937(Mersenne Twister),比rand()质量更高,生成的测试数据分布更均匀。
- 将代码保存为
- 1
信息
- ID
- 19328
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者