3 条题解
-
0
太棒了!自己编写数据生成器,是你从“答题者”蜕变为“出题人”的关键一步。
对于这道题,我们面临和之前一样的情况:题目原定的数据范围 实在太温柔了!在这个范围内, 的双重循环暴力搜索最多只需执行 次,任何正常的评测机都会让它瞬间拿到 100 分。
为了满足你**“让暴力解法能部分 AC 但不能全部 AC”的要求,我们在保证前几个测试点完全符合原题意的前提下,必须在最后几个测试点偷偷“超纲”**,把 和 放大到 级别。在这个级别下:
- 新手暴力解法 需要查找 次,绝对会因为超时(TLE)而挂掉。
- 标答哈希解法 只需要 次操作,依然 毫秒极速 AC!
同时,为了满足你对**文件大小(理想值 2MB 内)**的要求, 个整数以空格分隔,产生的文件大小约为
500 KB ~ 600 KB,完美符合极其轻量化的标准!
核心设计思路(10 个测试点分布)
- Test 1 (
N=3):样例 1。完全乱序但全员到齐。 - Test 2 (
N=3):样例 2。极端重复报数,大量缺席。 - Test 3 (
N=2):极小边界。班上只有两人,且只有一人报数。 - Test 4 (
N=100):局部重复。前 50 次全报 0,后 50 次全报 1,测试去重逻辑。 - Test 5 (
N=500):常规区间。所有人刚好都报了一次(全员到齐),打乱顺序。 - Test 6 (
N=1000):符合原题最大数据限制。纯随机缺席测试。 - Test 7 (
N=1000):符合原题最大数据限制。全员到齐的随机报数。 - Test 8 (
N=50000):【区分度杀手数据 1】。开始卡时间!纯随机报数,包含大量缺席。暴力法需要执行 次对比,极大概率 TLE。 - Test 9 (
N=100000):【区分度杀手数据 2】。十万级别全员到齐打乱顺序。暴力法需要执行 次对比,必定 TLE! - Test 10 (
N=100000):【终极杀手数据】。十万级别,但所有人全都报了同一个人的编号N-1!这是暴力法双重循环的最坏情况(每次内层遍历都要一直走到最后才能判断没找到),专门惩罚没写break或算法低效的代码。
数据生成器完整 C++ 代码 (
generator.cpp)请将以下代码保存为
generator.cpp,本地编译运行即可生成完美的测试数据。#include <iostream> #include <fstream> #include <vector> #include <string> #include <random> #include <algorithm> #include <numeric> using namespace std; // 标程:极速 O(N+M) 哈希桶算法 // 返回一个 vector,如果全员到达,返回一个特殊的 {-1} 标志 vector<int> solve(int n, int m, const vector<int>& reports) { vector<bool> present(n, false); int count = 0; for (int x : reports) { if (!present[x]) { present[x] = true; count++; } } if (count == n) { return {-1}; // 代表全员到齐 } vector<int> missing; for (int i = 0; i < n; ++i) { if (!present[i]) { missing.push_back(i); } } return missing; } int main() { // 优化系统 IO 速度 ios::sync_with_stdio(false); cin.tie(nullptr); mt19937 rng(42); // 固定随机种子以保证每次生成的数据一致 vector<int> Ns = {3, 3, 2, 100, 500, 1000, 1000, 50000, 100000, 100000}; vector<int> Ms = {3, 5, 2, 100, 500, 1000, 1000, 50000, 100000, 100000}; vector<vector<int>> reports(10); // Test 1: 样例 1 (0 2 1) reports[0] = {0, 2, 1}; // Test 2: 样例 2 (0 0 0 0 0) reports[1] = {0, 0, 0, 0, 0}; // Test 3: 极小边界 reports[2] = {1, 1}; // Test 4: 极端两极分化 reports[3].assign(100, 0); for(int i = 50; i < 100; ++i) reports[3][i] = 1; // Test 5: 所有人刚好一次,打乱顺序 reports[4].resize(500); iota(reports[4].begin(), reports[4].end(), 0); // 填充 0~499 shuffle(reports[4].begin(), reports[4].end(), rng); // Test 6: 原题边界 1000 随机 for(int i = 0; i < 1000; ++i) reports[5].push_back(rng() % 1000); // Test 7: 原题边界 1000 全员到达打乱 reports[6].resize(1000); iota(reports[6].begin(), reports[6].end(), 0); shuffle(reports[6].begin(), reports[6].end(), rng); // Test 8: TLE杀手 - 50000 随机 for(int i = 0; i < 50000; ++i) reports[7].push_back(rng() % 50000); // Test 9: TLE杀手 - 100000 全员到达打乱 reports[8].resize(100000); iota(reports[8].begin(), reports[8].end(), 0); shuffle(reports[8].begin(), reports[8].end(), rng); // Test 10: 终极恶心数据 - 100000 次全都报最后一个人的编号 reports[9].assign(100000, 99999); // 开始生成文件 for (int i = 0; i < 10; ++i) { int N = Ns[i]; int M = Ms[i]; // --- 1. 生成 .in 文件 --- string in_filename = to_string(i + 1) + ".in"; ofstream fout_in(in_filename); if (!fout_in.is_open()) continue; fout_in << N << " " << M << "\n"; for (int j = 0; j < M; ++j) { fout_in << reports[i][j] << (j == M - 1 ? "" : " "); } fout_in << "\n"; fout_in.close(); // --- 2. 调用 O(N+M) 标程计算正确答案 --- vector<int> ans = solve(N, M, reports[i]); // --- 3. 生成 .out 文件 --- string out_filename = to_string(i + 1) + ".out"; ofstream fout_out(out_filename); if (!fout_out.is_open()) continue; if (ans.size() == 1 && ans[0] == -1) { fout_out << N << "\n"; // 全员到齐 } else { for (int j = 0; j < ans.size(); ++j) { fout_out << ans[j] << (j == ans.size() - 1 ? "" : " "); } fout_out << "\n"; } fout_out.close(); cout << "成功生成 Test " << (i + 1) << "\t | N = " << N << "\t | M = " << M << "\n"; } cout << "\n所有测试数据生成完毕!无递归防爆栈,10个测试点总文件大小约 1.5 MB。" << endl; return 0; }OJ 部署与防坑指南
- 头文件保障:代码中包含了
<numeric>,这是用来调用iota函数的(它可以快速给数组填入0, 1, 2... N-1),它是极度优雅且避免写大量for循环赋值的高效算法函数。配合<algorithm>里的shuffle使用,让数据生成器兼具高性能。 - 避免文件体积爆炸(符合你的要求):虽然 听起来是个巨大的数字,但在存储空间上,包含 10 万个整数加上空格文本文件,实际大小只有约
550 KB。所有的.in和.out加在一起大约在 上下,读取与评测速度在 OJ 系统中只需几毫秒。 - 彻底区分复杂度:把这套数据放到 OJ,设置 Time Limit 为
1000 ms(默认值)。你会惊喜地看到,使用数组直接映射(桶思想)的学生瞬间满分;而使用两层for循环遍历的学生,会在 Test 8, 9, 10 遭遇红色的Time Limit Exceeded。你作为教练就能直接抓住这批同学,给他们讲授“哈希映射”的真谛了!
信息
- ID
- 13925
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者