top1编程
← 返回题目
题解

最佳位置

1 条题解

  • 0
    @ 2026-8-5 10:24:13

    解题思路

    小朋友站在第 x 行、第 y 列,能拿到的金币分两部分:

    1. 第 x 行上的所有金币;
    2. 第 y 列上的所有金币。

    这里有个小陷阱:如果 (x, y) 这一格本身就有一枚金币,它既在第 x 行里、又在第 y 列里,被数了两次。真正能拿到的应该是:

    能拿到的金币 = 第 x 行的金币数 + 第 y 列的金币数 - (x, y) 这格有没有金币

    所以我们的做法是:

    第一步,先读入所有有金币的格子,顺便统计每一行有多少金币、每一列有多少金币,同时用一个二维标记数组记下哪些格子有金币。

    第二步,把每一个位置 (i, j) 都当作小朋友站的地方,按上面的公式算出能拿到的金币数,找出最大的那一个。

    第三步,题目要求:如果有多个位置能拿到的金币一样多,要输出行号最小的,行号一样就输出列号最小的。我们只要从第 1 行第 1 列开始,一行一行、一列一列地枚举,并且只有严格大于当前最大值时才更新,那么记录下来的位置一定就是行号列号都最小的那个。

    参考代码

    // P4519 最佳位置:找行列金币总和最大的位置
    #include <iostream>
    using namespace std;
    
    int main() {
        int r, c, n;
        cin >> r >> c >> n;
        int row[105] = {0}, col[105] = {0}; // row[i]第i行金币数, col[j]第j列金币数
        int coin[105][105] = {0};           // coin[x][y]=1表示该格有金币
        for (int i = 0; i < n; i++) {
            int x, y;
            cin >> x >> y;
            coin[x][y] = 1;
            row[x]++;
            col[y]++;
        }
        int best = -1, bx = 0, by = 0;
        for (int i = 1; i <= r; i++) {
            for (int j = 1; j <= c; j++) {
                // 该位置能拿到的金币 = 第i行金币 + 第j列金币 - 交叉点(重复的那1枚)
                int t = row[i] + col[j] - coin[i][j];
                if (t > best) { // 严格更大才更新,保证行号列号最小优先
                    best = t;
                    bx = i;
                    by = j;
                }
            }
        }
        cout << best << endl;
        cout << bx << "," << by << endl;
        return 0;
    }
    

    复杂度分析

    读入 n 个金币的位置并统计行列时,是 O(n)。枚举所有位置时,要把 r×c 个格子都看一遍,是 O(r×c)。r、c 最大都是 100,所以非常快,时间复杂度是 O(n + r×c)。用了一个二维标记数组和两个一维统计数组,空间复杂度是 O(r×c)。

    • 1