题解:洛谷 P6200 Super Paintball
【题目来源】
洛谷:P6200 [USACO07OCT] Super Paintball G - 洛谷
【题目描述】
奶牛们最近从玩具商那里,买来了一套仿真版彩弹游戏设备(类似于真人 CS)。Bessie 把她们玩游戏的草坪划分成了 \(N \times N\) 的矩阵(\(1 \leq N \leq 100\)),同时他算出了她的 \(K\) 个对手在草地上的位置(\(1 \leq K \leq 10^5\)),现在你需要帮 Bessie 算些东西。
在这个游戏中,奶牛们用一把枪向八个方向中的任意一个方向射出子弹,这八个方向分别是:正北,正南,正东,正西,东北,东南,西北,西南(东北指北偏东 \(45^\circ\),东南,西北,西南同理)。
Bessie 想要你算出,有多少个位置可以让她射到所有对手。特别地,Bessie 可以和她的某一个对手站在同一格子,这时候她可以射到和她同一格子的对手。
【输入】
第一行两个整数 \(N, K\)。
接下来 \(K\) 行,每行两个整数 \(R_i, C_i\),表示第 \(i\) 头奶牛在第 \(R_i\) 行第 \(C_i\) 列。可能有两个奶牛在同一位置上。
【输出】
输出 Bessie 可以选择的格子数目。
【输入样例】
4 3
2 1
2 3
4 1
【输出样例】
5
【核心思想】
-
问题分析:给定 \(N \times N\) 的矩阵和 \(K\) 个对手位置,求有多少个位置 \((i, j)\) 满足:从 \((i, j)\) 沿八个方向(正北、正南、正东、正西、东北、东南、西北、西南)中的至少一个方向,可以"看到"所有 \(K\) 个对手(同一格子也算射到)。本质是一个八方向射线覆盖判定问题。
-
算法选择:
- 暴力模拟 + 八方向扫描:对每个空位 \((i, j)\),沿八个方向逐格扫描,统计能看到的对手数量,若等于 \(K\) 则该位置合法
-
关键步骤:
- 标记对手:用二维数组 \(a[x][y] = 1\) 标记对手位置
- 遍历所有位置(\(i\) 从 \(1\) 到 \(N\),\(j\) 从 \(1\) 到 \(N\)):
- 初始化计数器 \(\text{cnt} = 0\)
- 同格判定:若 \(a[i][j] = 1\),\(\text{cnt}++\)(站在对手格子上可直接射到)
- 八方向扫描(每个方向用 \(k\) 从 \(1\) 到 \(N\) 递增步长):
- 正东:\((i, j+k)\),条件 \(j+k \leq N\)
- 正西:\((i, j-k)\),条件 \(j-k \geq 1\)
- 正北:\((i-k, j)\),条件 \(i-k \geq 1\)
- 正南:\((i+k, j)\),条件 \(i+k \leq N\)
- 西北:\((i-k, j-k)\),条件 \(i-k \geq 1\) 且 \(j-k \geq 1\)
- 东南:\((i+k, j+k)\),条件 \(i+k \leq N\) 且 \(j+k \leq N\)
- 西南:\((i+k, j-k)\),条件 \(i+k \leq N\) 且 \(j-k \geq 1\)
- 东北:\((i-k, j+k)\),条件 \(i-k \geq 1\) 且 \(j+k \leq N\)
- 每个方向遇到对手(\(a[\cdot][\cdot] = 1\))则 \(\text{cnt}++\)
- 判定合法性:若 \(\text{cnt} = K\),\(\text{ans}++\)
- 输出答案:\(\text{ans}\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N^3)\),\(N^2\) 个位置,每个位置八方向扫描最多 \(O(N)\)
- 空间复杂度:\(O(N^2)\),存储对手位置的二维数组
-
八方向射线扫描的核心思想:
- 方向向量枚举:用坐标增量 \((\Delta r, \Delta c)\) 表示八个方向,通过统一的方向循环减少代码冗余(当前代码为展开写法)
- 逐格延伸判定:从当前位置沿某方向逐格检查,直到越界,统计路径上的对手数量
- 同格特殊处理:站在对手位置上时直接计数,体现"可以射到同一格子对手"的规则
- 暴力可行性:\(N \leq 100\),\(N^3 = 10^6\) 完全可接受,无需复杂优化
- 适用于棋盘覆盖、射线检测、视野判定类问题
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, k, a[105][105], ans=0;
int main()
{
cin >> n >> k; // 输入n和k
for (int i=1; i<=k; i++) { // 输入k个对手位置
int x, y;
cin >> x >> y;
a[x][y] = 1; // 每个位置标记为1
}
for (int i=1; i<=n; i++) { // 遍历N*N矩阵
for (int j=1; j<=n; j++) {
int cnt = 0;
if (a[i][j]==1) cnt++; // 如果站在对手的格子上,统计值加1
for (int k=1; k<=n; k++) { // 向左扫描
if (j-k>=1 && a[i][j-k]==1 ) cnt++;
}
for (int k=1; k<=n; k++) { // 向右扫描
if (j+k<=n && a[i][j+k]==1) cnt++;
}
for (int k=1; k<=n; k++) { // 向上扫描
if (i-k>=1 && a[i-k][j]==1) cnt++;
}
for (int k=1; k<=n; k++) { // 向下扫描
if (i+k<=n && a[i+k][j]==1) cnt++;
}
for (int k=1; k<=n; k++) { // 向左上扫描
if (i-k>=1 && j-k>=1 && a[i-k][j-k]==1) cnt++;
}
for (int k=1; k<=n; k++) { // 向右下扫描
if (i+k<=n && j+k<=n && a[i+k][j+k]==1) cnt++;
}
for (int k=1; k<=n; k++) { // 向左下扫描
if (i+k<=n && j-k>=1 && a[i+k][j-k]==1) cnt++;
}
for (int k=1; k<=n; k++) { // 向右上扫描
if (i-k>=1 && j+k<=n && a[i-k][j+k]==1) cnt++;
}
if (cnt==k) { // 如果统计值为k,说明所有对手都可以给射到
ans++; // 位置数加1
}
}
}
cout << ans << endl; // 输出位置数
return 0;
}
【运行结果】
4 3
2 1
2 3
4 1
5
浙公网安备 33010602011771号