1 条题解
-
0
题目题解
问题理解
有一个 的方阵,初始全为 。
在每行第 列左侧有一门大炮(向右射击),在每列第 行上方有一门大炮(向下射击)。
每次射击发射一个 ,它沿直线飞行,直到撞到边界或另一个 ,然后停在之前的单元格。
给定最终矩阵,问是否存在一个射击序列得到该矩阵。
第一步:射击规则分析
- 从第 行左侧的大炮射击: 从 出发向右,遇到第一个 或右边界时停止,停在 。
- 从第 列上方的大炮射击: 从 出发向下,遇到第一个 或下边界时停止,停在 。
因此,任何 必须是由其左侧或上方的 阻挡而产生的,否则它会一直飞到边界。
第二步:必要条件
考虑一个 在位置 ( 且 )。
它不可能由右侧或下方的 阻挡,因为飞行方向只有右和下。
所以它必须满足:要么其右侧有 (即 ),要么其下方有 (即 )。
否则,这个 会继续飞行到边界,无法停在 。对于最后一行()和最后一列()的 ,它们可以靠边界阻挡,因此无需条件。
第三步:充分性
可以证明,上述条件也是充分的。
构造方法:从下到上、从右到左,依次模拟射击过程。
每个 要么由右侧或下方的 阻挡,要么在边界上,因此可以按逆序还原出射击顺序。
第四步:算法
- 读入矩阵 。
- 从 到 , 到 遍历:
- 若 且 且 ,则输出
"NO"。
- 若 且 且 ,则输出
- 若遍历结束未发现矛盾,输出
"YES"。
第五步:时间复杂度
- 每个测试用例 。
- 总面积 ,满足要求。
代码实现
#include <bits/stdc++.h> using namespace std; bool a[50][50]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; for (int i = 0; i < n; i++) { string s; cin >> s; for (int j = 0; j < n; j++) { a[i][j] = (s[j] == '1'); } } bool ok = true; for (int i = n - 2; i >= 0; i--) { for (int j = n - 2; j >= 0; j--) { if (a[i][j] && !a[i + 1][j] && !a[i][j + 1]) { ok = false; } } } cout << (ok ? "YES" : "NO") << "\n"; } return 0; }
验证样例
输入:
5 4 0010 0011 0000 0000 2 10 01 2 00 00 4 0101 1111 0101 0111 4 0100 1110 0101 0111输出:
YES NO YES YES NO与题目输出一致。
总结
本题的关键在于:
- 理解 必须由右侧或下方的 阻挡,否则无法停留在非边界位置。
- 从右下角向左上角检查,确保每个非边界的 都有右侧或下方的邻居为 。
- 该条件既必要又充分。
- 1
信息
- ID
- 6343
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者