这道题目要求我们在一个N×N的点阵中,找到由John的牛(标记为'J')组成的最大正方形。题目允许我们额外放置一头牛Bessie到一个空点(标记为'*')上,这意味着正方形可以由3头John的牛和1头Bessie组成。
首先我们需要明确几个关键点:
最直观的解法是枚举所有可能的正方形,然后检查其顶点是否符合要求。对于一个N×N的网格,我们需要考虑:
这种方法的复杂度是O(N^4),对于N≤100的情况是可行的(100^4=100,000,000次操作,现代计算机可以在合理时间内完成)。
我们使用一个二维数组a[MAXN][MAXN]来存储网格信息:
a[i][j] = 1 表示该点是John的牛a[i][j] = 0 表示该点是空的a[i][j] = -1 表示该点是Bob的牛cpp复制for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int x = i; x <= n; x++) {
for (int y = j; y <= n; y++) {
// 计算另外两个顶点坐标
x1 = x - y + j;
x2 = i - y + j;
y1 = y + x - i;
y2 = j + x - i;
// 检查坐标是否合法
if (inMap(x1, y1) && inMap(x2, y2)) {
// 检查顶点是否有Bob的牛
if (a[i][j] < 0 || a[x][y] < 0 || a[x1][y1] < 0 || a[x2][y2] < 0) continue;
// 检查是否有至少3个John的牛
if ((a[i][j] + a[x][y] + a[x1][y1] + a[x2][y2]) >= 3) {
// 计算面积
s = (x - i) * (x - i) + (y - j) * (y - j);
ans = max(ans, s);
}
}
}
}
}
}
给定两个点(i,j)和(x,y),如何计算正方形的另外两个顶点?
这就是代码中x1,y1,x2,y2的计算公式的来源。
cpp复制for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
c = getchar();
if (c == '\n') c = getchar(); // 处理换行符
if (c == 'J') a[i][j]++;
if (c == 'B') a[i][j] = -1;
}
}
这里需要注意处理换行符,因为每行输入后有一个换行符,我们需要跳过它。
cpp复制bool inMap(int x, int y) {
return x >= 1 && x <= n && y >= 1 && y <= n;
}
这个简单的函数用于检查坐标是否在有效范围内。
正方形的面积等于边长的平方。由于我们存储的是顶点坐标,边长可以通过勾股定理计算:
cpp复制s = (x - i) * (x - i) + (y - j) * (y - j);
这里实际上计算的是边长的平方的两倍(因为(x-i)和(y-j)是直角边),但由于我们只需要比较大小,所以不影响结果。
当前算法的时间复杂度是O(N^4),对于N=100的情况,最坏情况下需要执行约100,000,000次循环。这在现代计算机上是可以接受的,但仍有优化空间。
在实际编程比赛中,建议:
除了题目给出的样例,还应该测试:
这种方法可以扩展到解决:
这类几何问题在实际中有广泛应用,如:
对于想深入学习的同学,建议:
在实际编码过程中,我发现以下几点特别重要:
提示:在竞赛编程中,建议先写出暴力解法确保正确性,然后再考虑优化。过早优化往往会导致更多错误。
对于这道题,最关键的insight是理解如何通过两个点计算出正方形的另外两个顶点。这个几何变换需要一定的数学直觉,建议在纸上多画图帮助理解。
最后,对于N=100的情况,虽然O(N^4)的复杂度看起来很高,但在实际运行中由于有很多提前终止的条件,实际运行时间是可以接受的。这也是竞赛编程中常见的"看似暴力实则可行"的解法。