题解:洛谷 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
【核心思想】
-
问题分析:给定 \(N \times N\) 网格,每个格子为
J(John的牛)、B(Bob的牛/障碍)或*(空地)。John 可以用自己的牛作为顶点形成正方形(不一定与坐标轴平行),Bessie 可以放在任意空地上。求最大正方形面积。这是一个枚举对角线 + 几何推导问题,关键在于利用正方形对角线互相垂直平分的性质,由两个已知顶点推导另外两个顶点。 -
算法选择:
- 枚举对角线端点:正方形的任意一条对角线确定后,另外两个顶点可由中点和向量旋转唯一确定
- 几何推导:设对角线端点为 \((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占据
-
关键步骤:
- 读入网格:\(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\)
- 读入网格:\(N\) 和 \(N \times N\) 网格,记录所有
-
时间/空间复杂度:
- 时间复杂度:\(O(J^2)\),\(J\) 为
J的数量,最多 \(N^2 = 10^4\) - 空间复杂度:\(O(N^2)\),存储网格和
J的坐标
- 时间复杂度:\(O(J^2)\),\(J\) 为
-
几何枚举的核心思想:
- 对角线确定正方形:正方形可由一条对角线唯一确定(另一对角线与之垂直平分且等长),避免枚举四个顶点的高复杂度
- 向量旋转公式:将向量 \((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
浙公网安备 33010602011771号