L2-3 小 Z 打地鼠
题目描述
又逢周日,小 Z 打开了一款打地鼠游戏。
游戏包含一个 n × m n\times m n×m 的网格地图,地图上每个格点都有一个地鼠,每个地鼠都有一个得分。
记第 x x x 行第 y y y 列的格点坐标为 ( x , y ) (x, y) (x,y),其中 1 ≤ x ≤ n 1\leq x\leq n 1≤x≤n, 1 ≤ y ≤ m 1\leq y\leq m 1≤y≤m,记 ( x , y ) (x, y) (x,y) 格点的地鼠的得分为 V ( x , y ) V(x, y) V(x,y)(可以为负数)。
小 Z 用一把旋转了 45 45 45 度的正方形锤子打地鼠,假设小 Z 落锤的位置为 ( x 0 , y 0 ) (x_0, y_0) (x0,y0),则所有坐标满足 ∣ x − x 0 ∣ + ∣ y − y 0 ∣ ≤ d |x - x_0| + |y - y_0|\leq d ∣x−x0∣+∣y−y0∣≤d 的位置 ( x , y ) (x, y) (x,y) 的地鼠都会被打掉。
例如:设
n
=
10
n = 10
n=10,
m
=
15
m = 15
m=15,
d
=
2
d = 2
d=2。蓝色(左上角位置)、青色(中间位置)、黄色(右下角位置)分别表示三种落锤情况下能够打到的格点。
一次落锤获得的得分是被打到的地鼠的得分的总和,小 Z 想要最大化得分,请你帮他算一算他能够得到的最大得分,以及所有可以得到这个得分的落锤位置坐标。
输入格式
第一行包含三个整数
n
,
m
,
d
n, m, d
n,m,d,与题目描述中所指一致。
接下来
n
n
n 行,每行
m
m
m 个数,表示每个格点上的地鼠的得分。
输出格式
第一行输出两个整数 m a x V a l u e , a n s C o u n t \rm maxValue, ansCount maxValue,ansCount,中间用空格隔开,分别表示能够得到的最大得分和可以得到这个得分的落锤位置的数量。
接下来 a n s C o u n t \rm ansCount ansCount 行,每行包含两个整数 x i , y i x_i, y_i xi,yi,表示一个落锤位置坐标。
你需要按照 ( x , y ) (x, y) (x,y) 升序的顺序输出,即 x x x 较小的在前,若 x x x 相同,则 y y y 较小的在前。
样例 #1
样例输入 #1
4 6 1
-3 4 5 4 3 2
1 6 1 7 5 2
9 9 0 0 8 -3
8 8 4 4 -3 2
样例输出 #1
32 1
3 2
提示说明
样例解释:
落锤位置为 ( 3 , 2 ) (3, 2) (3,2),能够打到 ( 2 , 2 ) , ( 3 , 1 ) , ( 3 , 2 ) , ( 3 , 3 ) , ( 4 , 2 ) (2, 2), (3, 1), (3, 2), (3, 3), (4, 2) (2,2),(3,1),(3,2),(3,3),(4,2) 位置的五个地鼠,得分为 6 + 9 + 9 + 0 + 8 = 32 6 + 9 + 9 + 0 + 8 = 32 6+9+9+0+8=32。
可以证明不存在其他落锤位置,使得得分 ≥ 32 \geq 32 ≥32。
数据规模与约定
对于
40
%
40\%
40% 的数据,保证
1
≤
n
,
m
≤
50
1\leq n, m\leq 50
1≤n,m≤50,
1
≤
d
≤
10
1\leq d\leq 10
1≤d≤10。
对于
60
%
60\%
60% 的数据,保证
1
≤
n
,
m
≤
400
1\leq n, m\leq 400
1≤n,m≤400,
1
≤
d
≤
10
1\leq d\leq 10
1≤d≤10。
对于
80
%
80\%
80% 的数据,保证
1
≤
n
,
m
≤
600
1\leq n, m\leq 600
1≤n,m≤600,
1
≤
d
≤
50
1\leq d\leq 50
1≤d≤50。
对于
100
%
100\%
100% 的数据,保证
1
≤
n
,
m
≤
1000
1\leq n, m\leq 1000
1≤n,m≤1000,
1
≤
d
≤
200
1\leq d\leq 200
1≤d≤200,
∣
V
(
x
,
y
)
∣
≤
50000
|V(x, y)|\leq 50000
∣V(x,y)∣≤50000。
思路提示
对于 40 % 40\% 40%的数据,枚举每个落锤位置,枚举所有格点能否被打到,计算答案,时间复杂度 O ( n 2 m 2 ) O(n^2m^2) O(n2m2)。
对于 60 % 60\% 60% 的数据,枚举每个落锤位置,找到所有能被打到的格点,计算答案,时间复杂度 O ( n m d 2 ) O(nmd^2) O(nmd2)。
对于 80 % 80\% 80% 的数据,枚举每个落锤位置,对于每个能打到的行都要做区间求和,可以使用一维前缀和求解,时间复杂度 O ( n m d ) O(nmd) O(nmd) 。
对于 100 % 100\% 100% 的数据,将地图和锤子顺时针旋转 45 45 45 度,多出来的格子得分为 0 0 0 ,再枚举每个落锤位置,使用二维前缀和快速计算所有被打到的格点的得分之和,时间复杂度 O ( n m ) O(nm) O(nm) 。
代码内容
#include <bits/stdc++.h>
using namespace std;
const int N = 1210;
int n, m, d;
int g[N << 1][N << 1];
pair<int, int> getpos(pair<int, int> p)
{
int x = p.first, y = p.second;
return make_pair(x + y - 1, N + x - y);
}
int calc(int lx, int ly, int rx, int ry)
{
lx = max(lx, 1); ly = max(ly, 1);
return g[rx][ry] - g[rx][ly - 1] - g[lx - 1][ry] + g[lx - 1][ly - 1];
}
signed main()
{
scanf("%d%d%d", &n, &m, &d);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
int x; scanf("%d", &x);
pair<int, int> pos = getpos(make_pair(i, j));
g[pos.first][pos.second] = x;
}
}
cerr << clock() * 1.0 / CLOCKS_PER_SEC << '\n';
for(int i = 1; i <= n + m + d; i++)
for(int j = 1; j <= N + n + d; j++)
g[i][j] = g[i][j - 1] + g[i - 1][j] - g[i - 1][j - 1] + g[i][j];
int maxVal = -1e9;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
pair<int, int> pos = getpos(make_pair(i, j));
maxVal = max(maxVal, calc(pos.first - d, pos.second - d, pos.first + d, pos.second + d));
}
}
vector<pair<int, int>> ans;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
pair<int, int> pos = getpos(make_pair(i, j));
if(calc(pos.first - d, pos.second - d, pos.first + d, pos.second + d) == maxVal)
ans.emplace_back(i, j);
}
}
printf("%d %d\n", maxVal, (int)(ans.size()));
for(auto [x, y] : ans)
printf("%d %d\n", x, y);
return 0;
}