计蒜客 蒜头君打地鼠 (矩阵旋转 + 二维前缀和)
链接 : 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;
}
如要转载请注明转载出处:http://www.cnblogs.com/WArobot