题解:洛谷 P2867 Big Square

【题目来源】

洛谷:P2867 [USACO06NOV] Big Square S - 洛谷

【题目描述】

农民 John 的牛参加了一次和农民 Bob 的牛的竞赛。

他们在区域中画了一个 \(N\times N\) 的正方形点阵,两个农场的牛各自占据了一些点。当然不能有两头牛处于同一个点。

农场的目标是用自己的牛作为 \(4\) 个顶点,形成一个面积最大的正方形(不必须和边界平行)。

除了 Bessie 以外,John 其他的牛都已经放到点阵中去了,要确定 Bessie 放在哪个位置,能使得 John 的农场得到一个最大的正方形(Bessie 不是必须参与作为正方形的四个顶点之一)。

【输入】

\(1\) 行:一个单独的整数,\(N\)\(2 \leq N \leq 100\))。

\(2 \sim (N+1)\) 行:第 \(i-1\) 行使用 \(N\) 个字符描述区域的第 \(i\) 行。其中,J代表此点被 John 的牛占据,B 代表此点被 Bob 的牛占据,而 * 代表一个未被占据的点。输入保证至少有一个未被占据的点。

【输出】

输出一个整数,表示 John 的农场所能达到的最大面积。如果无法形成正方形,则输出 \(0\)

【输入样例】

6
J*J***
******
J***J*
******
**B***
******

【输出样例】

4

【核心思想】

  1. 问题分析:给定 \(N \times N\) 网格,每个格子为 J(John的牛)、B(Bob的牛/障碍)或 *(空地)。John 可以用自己的牛作为顶点形成正方形(不一定与坐标轴平行),Bessie 可以放在任意空地上。求最大正方形面积。这是一个枚举对角线 + 几何推导问题,关键在于利用正方形对角线互相垂直平分的性质,由两个已知顶点推导另外两个顶点。

  2. 算法选择

    • 枚举对角线端点:正方形的任意一条对角线确定后,另外两个顶点可由中点和向量旋转唯一确定
    • 几何推导:设对角线端点为 \((x_1, y_1)\)\((x_2, y_2)\),中点为 \((x_0, y_0)\),向量 \((dx, dy) = (x_0-x_1, y_0-y_1)\),则另外两个顶点为 \((x_0-dy, y_0+dx)\)\((x_0+dy, y_0-dx)\)
    • 合法性检查:验证推导出的顶点为整数坐标、在网格内、且不被 B 占据
  3. 关键步骤

    • 读入网格\(N\)\(N \times N\) 网格,记录所有 J 的坐标到 cow[]
    • 枚举对角线\(i\)\(1\)cur-1\(j\)\(i+1\)cur):
      • 计算中点 \((x_0, y_0)\) 和向量 \((dx, dy)\)
      • 推导另外两个顶点 \((x_3, y_3)\)\((x_4, y_4)\)
      • 整数检查\(x_3, y_3, x_4, y_4\) 必须为整数
      • 边界检查:四个顶点都在 \([1, N]\) 范围内
      • 障碍检查\((x_3, y_3)\)\((x_4, y_4)\) 不能是 B;不能同时是 *(至少一个为 J 或 Bessie 可放)
      • 计算面积 \(= (x_3-x_1)^2 + (y_3-y_1)^2\),更新最大值
    • 输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(J^2)\)\(J\)J 的数量,最多 \(N^2 = 10^4\)
    • 空间复杂度:\(O(N^2)\),存储网格和 J 的坐标
  5. 几何枚举的核心思想

    • 对角线确定正方形:正方形可由一条对角线唯一确定(另一对角线与之垂直平分且等长),避免枚举四个顶点的高复杂度
    • 向量旋转公式:将向量 \((dx, dy)\) 逆时针旋转 \(90^\circ\)\((-dy, dx)\),利用中点对称性推导另外两个顶点
    • 整数格点约束:网格坐标为整数,推导出的顶点必须为整数,利用浮点数取整判断
    • Bessie 的灵活性:Bessie 可放在任意空地,因此另外两个顶点可以是 J*,但不能是 B。若两个都是 *,则无法形成(没有 John 的牛作为顶点)
    • 适用于"网格中找最大正方形"的几何问题,核心在于对角线枚举和向量旋转推导

【算法标签】

普及+ #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;  // 定义点对类型
const int N = 105;
int n, cur, ans;  // n: 网格大小,cur: 牛的数量,ans: 最大正方形面积
char g[N][N];  // 网格
PII cow[10005];  // 存储J牛的坐标

int main()
{
    cin >> n;  // 输入网格大小
    for (int i = 1; i <= n; i++)  // 输入网格
        for (int j = 1; j <= n; j++)
        {
            cin >> g[i][j];
            if (g[i][j] == 'J')  // 如果是J牛
                cow[++cur] = {i, j};  // 记录坐标
        }

    double x1, x2, x3, x4, y1, y2, y3, y4, x0, y0, dx, dy;  // 坐标计算变量

    for (int i = 1; i < cur; i++)  // 枚举第一头J牛
    {
        for (int j = i + 1; j <= cur; j++)  // 枚举第二头J牛
        {
            x1 = cow[i].first, y1 = cow[i].second;  // 第一头牛坐标
            x2 = cow[j].first, y2 = cow[j].second;  // 第二头牛坐标

            x0 = (x1 + x2) / 2, y0 = (y1 + y2) / 2;  // 中点坐标
            dx = x0 - x1, dy = y0 - y1;  // 向量
            x3 = x0 - dy, y3 = y0 + dx;  // 计算第三个顶点
            x4 = x0 + dy, y4 = y0 - dx;  // 计算第四个顶点

            if (x3 != int(x3) || y3 != int(y3)) continue;  // 坐标不是整数
            if (x4 != int(x4) || y4 != int(y4)) continue;  // 坐标不是整数
            if (x3 < 1 || x3 > n || y3 < 1 || y3 > n) continue;  // 越界
            if (x4 < 1 || x4 > n || y4 < 1 || y4 > n) continue;  // 越界

            if (g[int(x3)][int(y3)] == '*' && g[int(x4)][int(y4)] == '*') continue;  // 两个顶点都是空地
            if (g[int(x3)][int(y3)] == 'B' || g[int(x4)][int(y4)] == 'B') continue;  // 顶点是岩石

            int area = (x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1);  // 正方形面积
            ans = max(ans, area);  // 更新最大面积
        }
    }
    cout << ans << endl;  // 输出最大正方形面积
    return 0;
}

【运行结果】

6
J*J***
******
J***J*
******
**B***
******
4
posted @ 2026-08-10 12:28  团爸讲算法  阅读(7)  评论(0)    收藏  举报