1 条题解
-
0
你好!我是你的OI教练。很高兴能带你分析这道 GESP 七级的样题《迷宫统计》。
虽然这道题挂着“七级”的头衔,但别被吓到了!从核心逻辑来看,它其实是一道非常基础的图论存储与二维数组操作的题目,属于“送分题”范畴。只要你细心,这道题拿满分是轻而易举的。
我们先拿出草稿纸,把题目中的逻辑画出来。
1. 读题关键词:你的“雷达”响了吗?
读题时,请圈出以下决定解题方向的关键词:
- “ 行,每行 个整数”:
- 这描述的是图论中经典的邻接矩阵(Adjacency Matrix)。
- 第 行第 列的数字,代表从 到 是否连通。
- “ 号迷宫可以直接到达其他的迷宫”:
- 对应矩阵的第 行。
- 在图论中,这对应**出度(Out-degree)**的概念(但这题包含了自环)。
- “有多少迷宫可以直接到达 号迷宫”:
- 对应矩阵的第 列。
- 在图论中,这对应**入度(In-degree)**的概念。
- “注意...总可以直接到达自身”:
- 这意味着矩阵的对角线( 行 列)应该是 。
- 统计时不要漏掉自己到自己的情况。
2. 预备知识点
要解决这道题,你需要掌握:
- 二维数组:用于存储 的迷宫关系矩阵。
- 循环遍历:
- 遍历一行:固定行号,变列号。
- 遍历一列:固定列号,变行号。
3. 启发式教学:草稿纸上的推演
假设输入如下(样例):
矩阵(行号从1开始):
列1 列2 列3 列4 列5 列6 行1: 1 1 0 1 0 0 行2: 0 1 1 0 0 0 行3: 1 0 1 0 0 1 行4: 0 0 1 1 0 1 <-- 关注这一行 行5: 0 0 0 1 1 0 行6: 1 0 0 0 1 1 ^ | 关注这一列步骤一:统计 能去哪里(出度)
- 我们看第 4 行:
0 0 1 1 0 1 - 数值为 1 的列是:3, 4, 6。
- 数量 = 3。
步骤二:统计谁能去 (入度)
- 我们看第 4 列:
- 行1:
1(Yes) - 行2:
0 - 行3:
0 - 行4:
1(Yes) - 行5:
1(Yes) - 行6:
0
- 行1:
- 来源是:1, 4, 5。
- 数量 = 3。
步骤三:求和
- 总和 = 。
注意: 到 (自己到自己)在第一步统计了一次,在第二步又统计了一次。题目问的是“三个整数”,我们分别计算并输出即可,不需要去重(题目问的是两个统计量的和,直接相加)。
4. 标准代码 (NOIP C++14)
/** * 题目:P10265 [GESP样题 七级] 迷宫统计 * 考点:二维数组、邻接矩阵、行列遍历 * 难度:入门/普及- */ #include <iostream> #include <vector> using namespace std; // 数据范围 N <= 1000 // 开一个 1005 * 1005 的二维数组,int 或 bool 都可以 // 放在全局变量区,防止栈溢出 int maze[1005][1005]; void solve() { int n, m; // 1. 输入 N 和 M if (!(cin >> n >> m)) return; // 2. 输入邻接矩阵 // 注意:题目中的迷宫编号是 1 到 n // 我们数组下标也使用 1 到 n,方便对应,不使用 0 行 0 列 for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cin >> maze[i][j]; } } // 3. 统计 m 号迷宫可以到达的数量 (第 m 行) int count_out = 0; // 出度 for (int j = 1; j <= n; j++) { if (maze[m][j] == 1) { count_out++; } } // 4. 统计可以直接到达 m 号迷宫的数量 (第 m 列) int count_in = 0; // 入度 for (int i = 1; i <= n; i++) { if (maze[i][m] == 1) { count_in++; } } // 5. 输出结果 // 三个整数:出度数,入度数,总和 cout << count_out << " " << count_in << " " << (count_out + count_in) << endl; } int main() { // IO优化,虽然本题数据量不大,但养成好习惯 ios::sync_with_stdio(false); cin.tie(0); solve(); return 0; }5. 易错点与关键点注释
- 下标问题:
- 题目给的编号是 。代码中数组下标最好也从 1 开始循环,这样直观不易错。如果习惯用 0 开始,记得读取 后要执行
m--。
- 题目给的编号是 。代码中数组下标最好也从 1 开始循环,这样直观不易错。如果习惯用 0 开始,记得读取 后要执行
- 对角线检查:
- 题目特别说明“自己总可以直接到达自己”。
- 通常输入的矩阵对角线(
maze[i][i])都会是 1。 - 我们的代码直接统计矩阵中的 1 即可,无需额外特殊处理(因为样例解释中依据的就是矩阵数据)。
- 数据范围:
- ,二维数组 约占 4MB 内存,远小于比赛限制(通常 128MB 或 256MB),可以直接开二维数组。
6. 数据生成器 (1.in - 10.in)
这是一个用于生成测试数据的代码。为了覆盖各种情况,我们设置了不同规模的数据,并确保对角线为 1。
/** * P10265 数据生成器 * 生成 1.in/1.out ~ 10.in/10.out */ #include <iostream> #include <fstream> #include <vector> #include <cstdlib> #include <ctime> #include <string> using namespace std; // ================= 标准题解逻辑 ================= void solve_std(int n, int m, const vector<vector<int>>& grid, ofstream& fout) { int count_out = 0; for (int j = 1; j <= n; j++) { if (grid[m][j] == 1) count_out++; } int count_in = 0; for (int i = 1; i <= n; i++) { if (grid[i][m] == 1) count_in++; } fout << count_out << " " << count_in << " " << (count_out + count_in) << endl; } // ============================================== int randInt(int l, int r) { return rand() % (r - l + 1) + l; } void makeData(int id) { string inName = to_string(id) + ".in"; string outName = to_string(id) + ".out"; ofstream fin(inName); ofstream fout(outName); int N; // 梯度设置数据规模 if (id <= 3) N = 10; // 小数据 else if (id <= 6) N = 100; // 中等数据 else N = 1000; // 大数据 int M = randInt(1, N); fin << N << " " << M << endl; // 使用 vector 存储矩阵,下标 1~N vector<vector<int>> grid(N + 1, vector<int>(N + 1)); for (int i = 1; i <= N; i++) { for (int j = 1; j <= N; j++) { if (i == j) { // 题目保证:自己总可以直接到达自己 grid[i][j] = 1; } else { // 其他情况随机 0 或 1 // 可以通过调整概率来控制稀疏/稠密 // 这里设为 30% 概率有边 grid[i][j] = (randInt(1, 100) <= 30) ? 1 : 0; } fin << grid[i][j] << (j == N ? "" : " "); } fin << endl; } // 生成输出 solve_std(N, M, grid, fout); fin.close(); fout.close(); cout << "Generated Case " << id << " (N=" << N << ")" << endl; } int main() { srand((unsigned)time(0)); for (int i = 1; i <= 10; i++) { makeData(i); } return 0; }这段代码会生成 10 组测试数据,从小规模到最大规模 ,并且严格遵循题目中“自己可达自己”的约定。你可以用它来测试你的程序。加油!
- “ 行,每行 个整数”:
- 1
信息
- ID
- 14776
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者