2 条题解
-
0
你好!我是金牌教练。为了方便你快速搭建 OJ 测试环境,我为你编写了一个集成化的数据生成器与标程(Generator & Solver)。
这个程序将按照 NOI 竞赛的标准,生成 10 组测试点。设计思路是:前两组数据极小,暴力可过;中间增加边界条件;后四组数据达到极限规模,必须使用 的二分答案算法。
一、 测试点设计说明
测试点 规模 规模 规模 特点 1-2 100 200 100 极小规模,暴力算法可通过 3-4 1,000 2,000 中等规模,暴力算法开始超时 5 边界条件:,答案必为 6 边界条件: 极大,答案必为 7 特殊情况:所有元素相等,测试计算精度 8-10 极限数据:大数值随机分布
二、 数据生成器 C++ 代码
该程序采用 C++14 标准。它会自动运行标程逻辑并产生对应的
.in和.out文件。#include <iostream> #include <fstream> #include <vector> #include <string> #include <random> #include <algorithm> #include <ctime> using namespace std; typedef long long ll; // --- 标程:最优解 (二分答案) --- bool check(ll k, ll h, int n, const vector<int>& p) { if (k == 0) return false; ll total = 0; for (int i = 0; i < n; i++) { // 向上取整公式,防止浮点误差 total += (p[i] + k - 1) / k; // 剪枝优化:如果当前小时数已经超过 h,直接返回失败,防止 long long 溢出 if (total > h) return false; } return total <= h; } int solve(int n, ll h, const vector<int>& p) { int max_val = 0; for (int x : p) if (x > max_val) max_val = x; int left = 1, right = max_val, ans = max_val; while (left <= right) { int mid = left + (right - left) / 2; if (check(mid, h, n, p)) { ans = mid; right = mid - 1; } else { left = mid + 1; } } return ans; } // --- 数据构造器 --- void generate_test_cases() { mt19937 rng(time(0)); for (int t = 1; t <= 10; t++) { string in_name = to_string(t) + ".in"; string out_name = to_string(t) + ".out"; ofstream fout(in_name); ofstream fans(out_name); int n; ll h; int max_p_range; // 根据测试点设计矩阵设定范围 if (t <= 2) { n = 100; h = 200; max_p_range = 100; } else if (t <= 4) { n = 1000; h = 2000; max_p_range = 100000; } else { n = 10000; max_p_range = 1000000000; } if (t == 5) h = n; // 边界:h=n else if (t == 6) h = 1000000000; // 边界:h极大 else if (t > 6) h = n + rng() % 100000000; vector<int> piles(n); if (t == 7) { // 边界:全相等 int val = rng() % 1000000000 + 1; piles.assign(n, val); h = 2 * n; } else { for (int i = 0; i < n; i++) { piles[i] = rng() % max_p_range + 1; } } // 1. 写入输入文件 (NOI 格式) fout << n << " " << h << "\n"; for (int i = 0; i < n; i++) { fout << piles[i] << (i == n - 1 ? "" : " "); } fout << endl; // 2. 运行标程并写入输出文件 int result = solve(n, h, piles); fans << result << endl; cout << "Generated Case " << t << ": n=" << n << " h=" << h << " ans=" << result << endl; fout.close(); fans.close(); } } int main() { cout << "Starting data generation for [koko]..." << endl; generate_test_cases(); cout << "All test cases generated successfully." << endl; return 0; }
三、 技术说明与优化细节
-
文件大小控制:
- 本题最大的数据规模是 ,即使每个香蕉堆都是 (10位数字),加上空格,每一行大约只有 110,000 字节(约 107 KB)。
- 10 组输入输出文件的总大小约为 1.1 MB,完全符合你要求的 2MB 以内的理想值。
-
安全计算与剪枝:
- 防止除以零:在二分查找中,速度 从 开始,确保
check函数中不会出现x / 0。 - 防止溢出:在
check函数累加小时数时,采用了long long。并且加入了一行关键剪枝:if (total > h) return false;。这在 较小而 很小时,能防止total累加到超出long long范围。
- 防止除以零:在二分查找中,速度 从 开始,确保
-
区分度设计:
- 暴力解法:如果选手直接从速度 开始枚举,时间复杂度是 。在测试点 3 以后,由于 达到 甚至 ,暴力算法会因 次运算而彻底卡死。
- 对拍建议:如果你作为教练需要测试选手的代码,请确保时限(Time Limit)设为 1.0s。
-
非递归实现:
- 所有的生成逻辑和标程逻辑均采用迭代(Iterative)写法,避免了在处理大规模数据时产生不必要的栈开销。
四、 如何使用
- 将上述代码存为
gen.cpp并编译:g++ gen.cpp -o gen -std=c++14。 - 执行
./gen,它会在当前目录下生成1.in,1.out...10.out。 - 将这些文件打包上传到你的 OJ 系统即可。
-
信息
- ID
- 19501
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 2
- 上传者