题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(N \times N\) 的矩阵和 \(K\) 个对手位置,求有多少个位置 \((i, j)\) 满足:从 \((i, j)\) 沿八个方向(正北、正南、正东、正西、东北、东南、西北、西南)中的至少一个方向,可以"看到"所有 \(K\) 个对手(同一格子也算射到)。本质是一个八方向射线覆盖判定问题。

  2. 算法选择

    • 暴力模拟 + 八方向扫描:对每个空位 \((i, j)\),沿八个方向逐格扫描,统计能看到的对手数量,若等于 \(K\) 则该位置合法
  3. 关键步骤

    • 标记对手:用二维数组 \(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}\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^3)\)\(N^2\) 个位置,每个位置八方向扫描最多 \(O(N)\)
    • 空间复杂度:\(O(N^2)\),存储对手位置的二维数组
  5. 八方向射线扫描的核心思想

    • 方向向量枚举:用坐标增量 \((\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
posted @ 2026-08-29 09:39  团爸讲算法  阅读(2)  评论(0)    收藏  举报