题解:AT_abc377_c [ABC377C] Avoid Knight Attack
题目传送门
题目大意
有一个
例如,放在
有多少个不会被马吃掉的棋子?
题目分析
我们发现,这道题目是前一题的加强版,可以用二维数组记录被马吃掉的位置。
但是,我们发现
空间复杂度:
对于开一个N\times N 的数组,不难得出时间复杂度为\mathcal{O}(N^2) 。时间复杂度:
输入并标记M 个位置,每个位置扩展出最多8 个点,可以同时计算,总用时\mathcal{O}(M) 。
于是,我们想到,利用 map 记录下所有能被马攻击的坐标 (包括马所在位置),最后进行计算就可以啦!
最坏空间复杂度
空间复杂度: 对于一个存储
M 个元素的双下标 map,每个位置有3 个元素,空间复杂度为\mathcal{O}(M) 。时间复杂度:
- 输入
M 个数,用时\mathcal{O}(M) ;- 每次扩展
8 个方向,用时\mathcal{O}(1) ;- 每次插入 map,用时
\mathcal{O}(\log_2M) 。综上所述,时间复杂度为
\mathcal{O}(M \log_2M) 。
我们可以打出八个方向的表,边输入边记录排除的格子。
Q:如何使用 map 记录二维坐标?\ A:使用 map 套用 pair。
代码有一些细节,需要注意: :::warning[注意]{open}
- 题目样例二给出了提示,要开 long long。
- 需要特判所记录格子坐标是否越界。
- 需要记录马的所在位置。
- 将
x 、y 坐标放入 map 时,需要用 make_pair 函数将 int 类型改为 pair。 :::
具体介绍请看代码中的注释。
代码实现
#include <bits/stdc++.h>
#define int long long // 提示:答案可能是2^32或更大
using namespace std;
map <pair <int, int>, bool> a; // 记录(x, y)位置放置棋子,是否能被马吃掉
int n, m, x, y;
int fx[] = {0, 2, 1, -1, -2, -2, -1, 1, 2}; // 打8个方向的表
int fy[] = {0, 1, 2, 2, 1, -1, -2, -2, -1};
signed main() {
cin >> n >> m;
for (int i = 1;i <= m;i++) {
cin >> x >> y;
a[make_pair(x, y)] = 1; // 注意:要记录马的所在位置
for (int j = 1;j <= 8;j++) // 枚举8个方向
if (x + fx[j] > 0 && y + fy[j] > 0 && x + fx[j] <= n && y + fy[j] <= n) // 特判:防止坐标越界
a[make_pair(x + fx[j], y + fy[j])] = 1; // 将这个位置设为可被吃掉
}
// 进行数学运算:所有格子-可攻击格子=剩余格子,a.size()代表 map 中被使用过的格子数
cout << n * n - a.size() << endl;
return 0;
}
update 2024.11.4:
- 对于时空复杂度的计算做详细介绍。
update 2025.07.29:
- 由于洛谷编辑器更新,优化题解格式;
- 优化了时空复杂度计算,删除了常数部分。
To 管理员:由于表述需要,因此使用折叠框,请不要介意。