1 条题解
-
0
这是一个完整的 C++ 数据生成器。它会生成 10 组测试数据(
1.in/1.out~10.in/10.out),涵盖了从极小数据、正交向量、成比例向量、精度测试到最大数据规模的各类情况。数据生成器设计思路
- Solver 类:内置了一个标准的 AC 解法,用于根据生成的
.in文件计算正确的.out答案。解法中使用double进行计算,遵循“严格大于则更新”的原则来处理 tie-breaking(相同最大值取最小编号)。 - 生成策略:
- Case 1: 极小规模 (),测试基本连通性。
- Case 2: 小规模随机,方便肉眼检查。
- Case 3: 正交测试。目标向量与某些候选向量垂直(点积为0,相似度0),测试零值处理。
- Case 4: 完美缩放。候选向量包含目标向量的整数倍(如 ),测试相似度为 1.0 的情况。
- Case 5: 平局测试(Tie-breaking)。设计多个候选向量与目标向量相似度完全相同,验证是否输出了最小编号。
- Case 6: 稀疏向量。包含大量 0,测试分母计算和 0 的处理。
- Case 7: 极近向量。候选向量之间差异极小,测试浮点数精度。
- Case 8: 全同向量。所有候选向量都一样。
- Case 9: 最大值边界。所有元素都是 100,测试数值溢出(虽然这题
int够用,但验证逻辑)。 - Case 10: 最大规模随机 ()。
完整代码 (
generator.cpp)#include <iostream> #include <fstream> #include <vector> #include <cmath> #include <algorithm> #include <random> #include <ctime> #include <string> #include <iomanip> using namespace std; // ========================================== // Part 1: 标准 AC 解法 (Solver) // ========================================== class Solver { public: int solve(int n, int m, const vector<int>& A, const vector<vector<int>>& B) { double maxSim = -2.0; // 余弦相似度最小是 -1,设 -2 安全 int ansIdx = 1; // 计算 A 的模长 double normA = 0; for (int x : A) normA += (long long)x * x; normA = sqrt(normA); for (int k = 0; k < m; ++k) { double dot = 0; double normB = 0; for (int i = 0; i < n; ++i) { dot += (long long)A[i] * B[k][i]; normB += (long long)B[k][i] * B[k][i]; } normB = sqrt(normB); // 题目保证模长不为0 double sim = dot / (normA * normB); // 题目要求:相似度最大;若相同,取编号最小。 // 因为我们是按编号 1..m 顺序遍历,只有 sim "严格大于" maxSim 时才更新 // 这样如果是 "等于",就不会更新,保留了之前的(较小的)编号 if (sim > maxSim) { maxSim = sim; ansIdx = k + 1; } } return ansIdx; } }; // ========================================== // Part 2: 数据生成工具箱 // ========================================== mt19937 rng(time(0)); // 确保向量不全为 0 (题目约束) void ensure_nonzero(vector<int>& v) { bool all_zeros = true; for(int x : v) if(x != 0) all_zeros = false; if (all_zeros) { uniform_int_distribution<int> idx_dist(0, v.size() - 1); uniform_int_distribution<int> val_dist(1, 100); v[idx_dist(rng)] = val_dist(rng); } } // 生成随机向量 vector<int> gen_vector(int n, int min_v, int max_v, double zero_prob = 0.0) { vector<int> v(n); uniform_int_distribution<int> val_dist(min_v, max_v); uniform_real_distribution<double> prob_dist(0.0, 1.0); for(int i=0; i<n; ++i) { if (prob_dist(rng) < zero_prob) v[i] = 0; else v[i] = val_dist(rng); } ensure_nonzero(v); return v; } // ========================================== // Part 3: 主程序 // ========================================== int main() { Solver solver; cout << "Generating test data for Cosine Similarity..." << endl; for (int i = 1; i <= 10; ++i) { string in_file = to_string(i) + ".in"; string out_file = to_string(i) + ".out"; ofstream fin(in_file); ofstream fout(out_file); int N, M; vector<int> A; vector<vector<int>> B; string desc; // ---- 生成策略 ---- switch(i) { case 1: // 极小值边界 N = 1; M = 1; A = {10}; B = {{5}}; desc = "Min constraints N=1, M=1"; break; case 2: // 小规模随机 N = 5; M = 5; A = gen_vector(N, 0, 10); for(int k=0; k<M; ++k) B.push_back(gen_vector(N, 0, 10)); desc = "Small Random"; break; case 3: // 正交测试 (dot product = 0) N = 2; M = 3; A = {1, 0}; // B1: 垂直 (0, 10), B2: 平行 (10, 0), B3: 45度 (1, 1) B = {{0, 10}, {10, 0}, {1, 1}}; desc = "Orthogonal and Parallel vectors"; break; case 4: // 缩放测试 (Sim = 1.0) N = 5; M = 4; A = {1, 2, 3, 4, 5}; B.push_back({2, 4, 6, 8, 10}); // 2倍,Sim=1.0 B.push_back({5, 1, 1, 1, 1}); // 杂乱 B.push_back({10, 20, 30, 40, 50}); // 10倍,Sim=1.0 B.push_back({0, 0, 0, 0, 1}); desc = "Scaled vectors (Sim=1.0)"; break; case 5: // 平局测试 (Tie Breaking) // 构造 B1 和 B3 相似度相同且最大,应输出 1 N = 2; M = 4; A = {1, 1}; B.push_back({2, 2}); // Sim = 1.0, ID=1 B.push_back({1, 0}); B.push_back({5, 5}); // Sim = 1.0, ID=3 B.push_back({0, 1}); desc = "Tie breaking (Output smallest ID)"; break; case 6: // 稀疏向量 N = 20; M = 20; A = gen_vector(N, 1, 10, 0.7); // 70% 概率为0 for(int k=0; k<M; ++k) B.push_back(gen_vector(N, 1, 10, 0.7)); desc = "Sparse vectors (Many zeros)"; break; case 7: // 精度测试 (Very close vectors) N = 3; M = 3; A = {100, 100, 100}; B.push_back({100, 100, 99}); // Slightly off B.push_back({100, 100, 100}); // Perfect B.push_back({99, 100, 100}); // Slightly off desc = "Precision test"; break; case 8: // 边界数值 (All 100) N = 10; M = 10; A = vector<int>(N, 100); for(int k=0; k<M; ++k) B.push_back(vector<int>(N, 100)); // 修改最后一个稍微不同 B.back()[0] = 1; desc = "Max values (All 100)"; break; case 9: // 一般随机 N = 50; M = 50; A = gen_vector(N, 0, 100); for(int k=0; k<M; ++k) B.push_back(gen_vector(N, 0, 100)); desc = "Medium Random"; break; case 10: // 最大规模 N = 100; M = 100; A = gen_vector(N, 0, 100); for(int k=0; k<M; ++k) B.push_back(gen_vector(N, 0, 100)); desc = "Max Constraints N=100, M=100"; break; } // ---- 写入输入文件 ---- fin << N << " " << M << endl; for(int k=0; k<N; ++k) fin << A[k] << (k==N-1 ? "" : " "); fin << endl; for(int i=0; i<M; ++i) { for(int k=0; k<N; ++k) { fin << B[i][k] << (k==N-1 ? "" : " "); } fin << endl; } // ---- 计算并写入输出文件 ---- int ans = solver.solve(N, M, A, B); fout << ans << endl; fin.close(); fout.close(); cout << "Generated Case " << i << ": " << desc << endl; } cout << "\nAll test cases generated successfully." << endl; return 0; }使用方法
- 将代码保存为
generator.cpp。 - 编译:
g++ generator.cpp -o generator。 - 运行:
./generator(Windows 下是generator.exe)。 - 程序运行结束后,当前目录下会生成
1.in/1.out到10.in/10.out。
数据说明
- 格式:完全符合 GESP 4级和 OI 题目格式。
- 约束:确保了所有向量模长不为 0(
ensure_nonzero函数),数值在[0, 100]之间。 - 边界覆盖:
- Tie-breaking(Case 5):这是此题最容易错的地方,生成器构造了两个完全一样的最大相似度,标准答案会输出较小的 ID,用来卡掉没有处理平局情况的代码。
- Scaled(Case 4):测试学生是否理解向量方向相同即相似度为 1。
- Max N/M(Case 10):测试程序的数组大小和计算效率。
- Solver 类:内置了一个标准的 AC 解法,用于根据生成的
- 1
信息
- ID
- 19352
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者