<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;
}