题解
最佳位置
1 条题解
-
0
解题思路
小朋友站在第 x 行、第 y 列,能拿到的金币分两部分:
- 第 x 行上的所有金币;
- 第 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