<Atcoder - 129D> vector应用
https://atcoder.jp/contests/abc129/tasks/abc129_d
题意:
给定一个由 . 和 # 组成的矩阵,在点处选择一个位置放灯,灯光可以在该点处向上下左右四个方向发射,但是碰到#就停止,问可以被灯光辐射到的最大点数。看下样例1,题意就清楚了,选(2,2)放灯,算上自己最多可以辐射8个点。
思路:
首先看到这种点和#组成的矩阵首先想到深搜和广搜,但是他只有源点是4个方向发射,被源点辐射到的点都是单一方向的,不适合dfs和bfs的使用条件,而且n和m都是2e3搜索可能会TLE,所以这种思路pass。那么接下来会想一种暴力的做法,枚举点的位置,看把当前的点作为放灯的源点,其所在行所在列分别能辐射到多少个点,然后取一个最大值,这样复杂度是n^3的依然会TLE。那么我们想办法优化这种想法,既然碰到#灯光的辐射就停止了,那么我们可以先预处理出#的位置,把第i行的#存进vec[i]中,即vec[i][j]表示第i行第j列是#。这样我们看当前的点在当前行处于哪两个#之间,这就是它水平方向能辐射的点,列方向也是同理。因此我们需要把边界都初始化成#,初始化pos = 0,如下图,(1,1)就是点,那么它处于 vec[1][0] = 0 和 vec[1][1] = 4 这两个#之间,所以它能辐射到的点就是4 - 0 - 1 = 3个。点(1,2)和点(1,3)也是同理,都介于vec[1][0] = 0 和 vec[1][1] = 4 这两个#之间。那么我现在走到(1,4)是#了,那么pos往前前进一位,即pos++,这样我走到(1,5)又是点了,点(1,5)就介于vec[1][1] = 4 和 vec[1][2] = 7之间(这也是我们初始化边界都为#的原因,目的是让每个点都能被两个#夹在中间,这样vec的相邻元素做差就能求出灯光辐射到的点的个数),这样点(1,5)水平方向能辐射到的点就是7 - 4 - 1 = 2个。这是水平方向,竖直方向同理,另一个vector存放第j列的#,依旧每列都先初始化pos = 0,看当前列找到的点在该列中处于那两个#之间。
看下核心代码:
(1) 这步是把#的坐标都预处理存进行和列的两个vector中,vec1[i]表示第i行的#的位置,vec2[j]表示第j列的#的位置
for(int i = 0; i <= n + 1; i++) {
for(int j = 0; j <= m + 1; j++) {
if(s[i][j] == '#') {
vec1[i].push_back(j);
vec2[j].push_back(i);
}
}
}
(2) 这步是枚举每行的点,看该行的点介于哪两个#之间
for(int i = 1; i <= n; i++) {
pos = 0;
for(int j = 1; j <= m; j++) {
if(s[i][j] == '.') {
res[i][j] += vec1[i][pos + 1] - vec1[i][pos] - 1; //相邻两个#之间.的个数
}
else pos++;
}
}
(3) 这步是枚举每列的点,看该列的点介于哪两个#之间
for(int j = 1; j <= m; j++) {
pos = 0;
for(int i = 1; i <= n; i++) {
if(s[i][j] == '.') {
res[i][j] += vec2[j][pos + 1] - vec2[j][pos] - 1; //相邻两个#之间.的个数
}
else pos++;
}
}
ps:每个点对应的的res[i][j]要减1之后再和ans比较,因为源点这个位置被行列都辐射了一遍,多算了一次。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxx = 2e3 + 7;
int n, m, ans;
char s[maxx][maxx];
int res[maxx][maxx];
vector <int> vec1[maxx]; //每行#坐标
vector <int> vec2[maxx]; //每列#坐标
int main() {
scanf("%d %d", &n, &m);
for(int i = 0; i <= n + 1; i++) { //目的是把边界都初始化成#
for(int j = 0; j <= m + 1; j++) s[i][j] = '#';
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) scanf(" %c", &s[i][j]);
}
for(int i = 0; i <= n + 1; i++) {
for(int j = 0; j <= m + 1; j++) {
if(s[i][j] == '#') {
vec1[i].push_back(j);
vec2[j].push_back(i);
}
}
}
int pos = 0;
for(int i = 1; i <= n; i++) {
pos = 0;
for(int j = 1; j <= m; j++) {
if(s[i][j] == '.') {
res[i][j] += vec1[i][pos + 1] - vec1[i][pos] - 1; //相邻两个#之间.的个数
}
else pos++;
}
}
pos = 0;
for(int j = 1; j <= m; j++) {
pos = 0;
for(int i = 1; i <= n; i++) {
if(s[i][j] == '.') {
res[i][j] += vec2[j][pos + 1] - vec2[j][pos] - 1; //相邻两个#之间.的个数
}
else pos++;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) ans = max(ans, res[i][j] - 1); //(i, j)位置的点算了两次
}
printf("%d\n", ans);
return 0;
}