鏈接 : Here!
思路 :
- 首先看數據範圍 $1\leq n \leq 2000$, $1 \leq k \leq 100$ , 直接暴力肯定 $T$, 如果錘子是正着的就好辦了, 就可用二維前綴和的技巧來進行降維了!
- 所以直接將矩陣右旋45°, 讓錘子正過來, 右旋時需要注意原座標 $(x, y)$ 被映射爲 $(x+y, n-1-x-y)$ 了, 原矩陣被放大爲$(2n-1) * (2n-1)$ 了, 同時錘子也被放大爲 $(2k-1) * (2k-1)$
- 直接掃描一遍二維數組, 計算一下二維前綴和, 然後枚舉錘子的四個角, $O(1)$ 的時間內便能找到錘子所在範圍中地鼠的個數了
注意 : 這個數據好像有毒, 按道理來說錘子不一定全部落在原矩形內砸到的地鼠最多, 很有可能原矩形只有一條邊上有地鼠, 因此如果想得到最大地鼠數的話, 錘子的一部分必須在外面, 但是我修改 $check$ 數組後反而通不過所有數據 QAQ
代碼 :
/*************************************************************************
> File Name: 蒜頭君打地鼠.cpp
> Author:
> Mail:
> Created Time: 2017年11月20日 星期一 19時58分35秒
************************************************************************/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define MAX_N 4000
int a[MAX_N][MAX_N];
int sum[MAX_N][MAX_N];
bool vis[MAX_N][MAX_N];
int n, k, t_n;
// 這個題有毒吧...
// 注意旋轉後矩陣和錘子都變大了
// 這裏旋轉後的點並不是嚴格遵循數學中的旋轉
void read() {
memset(a, 0, sizeof(a));
memset(sum, 0, sizeof(sum));
memset(vis, 0, sizeof(vis));
scanf("%d%d", &n, &k);
for (int i = 0 ; i < n ; ++i) {
for (int j = 0 ; j < n ; ++j) {
int temp;
scanf("%d", &temp);
a[i + j][n - 1 - i + j] = temp;
vis[i + j][n - 1 - i + j] = 1;
}
}
t_n = n;
n = n * 2 - 1;
k = k * 2 - 1;
// 可以清晰的查看到矩陣被放大了
// 旋轉後矩陣的大小變爲了原來的 (2 * n - 1) X (2 * n - 1)
// 當然錘子也應該變大
// 看起來是右旋45度
//
// TODO 拓展問題 : 如果將矩陣左旋45度,旋轉90度,旋轉180度...等等
}
void cal_sum() {
for (int i = 0 ; i < n ; ++i) {
for (int j = 0 ; j < n ; ++j) {
if (i > 0) sum[i][j] += sum[i - 1][j];
if (j > 0) sum[i][j] += sum[i][j - 1];
if (i > 0 && j > 0) sum[i][j] -= sum[i - 1][j - 1];
sum[i][j] += a[i][j];
}
}
}
// 檢查(x, y)是否合法
bool check(int x, int y) {
return (y <= x + t_n - 1) && (y >= x - t_n + 1) && (y >= -x + t_n - 1) && (y <= -x + (3 * t_n - 3));
}
int cal_max() {
int max_value = -1;
for (int i = 0 ; i < n && i + k - 1 < n ; ++i) {
for (int j = 0 ; j < n && j + k - 1 < n ; ++j) {
if (!(check(i, j) && check(i + k - 1, j) && check(i, j + k - 1) && check(i + k - 1, j + k - 1))) continue;
// 錘子一定要在原來的矩形內
if (!vis[i][j]) continue;
int now = sum[i + k - 1][j + k - 1];
if (i > 0) now -= sum[i - 1][j + k - 1];
if (j > 0) now -= sum[i + k - 1][j - 1];
if (i > 0 && j > 0) now += sum[i - 1][j - 1];
max_value = max(max_value, now);
}
}
return max_value;
}
void solve() {
cal_sum();
printf("%d\n", cal_max());
}
int main() {
read();
solve();
return 0;
}