1 条题解

  • 0
    @ 2025-12-9 12:17:02

    你好!我是你的OI教练。很高兴能带你分析这道 GESP 七级的样题《迷宫统计》。

    虽然这道题挂着“七级”的头衔,但别被吓到了!从核心逻辑来看,它其实是一道非常基础的图论存储二维数组操作的题目,属于“送分题”范畴。只要你细心,这道题拿满分是轻而易举的。

    我们先拿出草稿纸,把题目中的逻辑画出来。


    1. 读题关键词:你的“雷达”响了吗?

    读题时,请圈出以下决定解题方向的关键词:

    1. nn 行,每行 nn 个整数”
      • 这描述的是图论中经典的邻接矩阵(Adjacency Matrix)
      • ii 行第 jj 列的数字,代表从 iijj 是否连通。
    2. mm 号迷宫可以直接到达其他的迷宫”
      • 对应矩阵的mm
      • 在图论中,这对应**出度(Out-degree)**的概念(但这题包含了自环)。
    3. “有多少迷宫可以直接到达 mm 号迷宫”
      • 对应矩阵的mm
      • 在图论中,这对应**入度(In-degree)**的概念。
    4. “注意...总可以直接到达自身”
      • 这意味着矩阵的对角线(iiii 列)应该是 11
      • 统计时不要漏掉自己到自己的情况。

    2. 预备知识点

    要解决这道题,你需要掌握:

    • 二维数组:用于存储 N×NN \times N 的迷宫关系矩阵。
    • 循环遍历
      • 遍历一行:固定行号,变列号。
      • 遍历一列:固定列号,变行号。

    3. 启发式教学:草稿纸上的推演

    假设输入如下(样例): N=6,M=4N=6, M=4

    矩阵(行号从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
                       ^
                       |
                    关注这一列
    

    步骤一:统计 m=4m=4 能去哪里(出度)

    • 我们看第 4 行0 0 1 1 0 1
    • 数值为 1 的列是:3, 4, 6。
    • 数量 = 3

    步骤二:统计谁能去 m=4m=4(入度)

    • 我们看第 4 列
      • 行1: 1 (Yes)
      • 行2: 0
      • 行3: 0
      • 行4: 1 (Yes)
      • 行5: 1 (Yes)
      • 行6: 0
    • 来源是:1, 4, 5。
    • 数量 = 3

    步骤三:求和

    • 总和 = 3+3=63 + 3 = 6

    注意4444(自己到自己)在第一步统计了一次,在第二步又统计了一次。题目问的是“三个整数”,我们分别计算并输出即可,不需要去重(题目问的是两个统计量的和,直接相加)。


    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. 下标问题
      • 题目给的编号是 1n1 \dots n。代码中数组下标最好也从 1 开始循环,这样直观不易错。如果习惯用 0 开始,记得读取 mm 后要执行 m--
    2. 对角线检查
      • 题目特别说明“自己总可以直接到达自己”。
      • 通常输入的矩阵对角线(maze[i][i])都会是 1。
      • 我们的代码直接统计矩阵中的 1 即可,无需额外特殊处理(因为样例解释中依据的就是矩阵数据)。
    3. 数据范围
      • N1000N \le 1000,二维数组 1000×10001000 \times 1000 约占 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 组测试数据,从小规模到最大规模 N=1000N=1000,并且严格遵循题目中“自己可达自己”的约定。你可以用它来测试你的程序。加油!

    • 1

    信息

    ID
    14776
    时间
    1000ms
    内存
    32MiB
    难度
    3
    标签
    递交数
    1
    已通过
    1
    上传者