Part 0 传送门
Part 1 思路
如果攻击范围是正方形或长方形,那么最简单的方法是二维前缀和,但这道题攻击范围是三角形,也可以考虑使用二维前缀和。
因为向右上,右下,左上,左下攻击的攻击范围经过几次旋转都可以变成同一个三角形,所以我们不用一个一个去算,可以去旋转地图后再用同样的方法算。
那要怎么实现二维前缀和呢?
在下图中我们要求的是绿色三角形(即攻击范围),绿色三角形=大三角形-红色梯形-蓝色梯形+黄色长方形,那么问题就变成了怎么求梯形,长方形和三角形的前缀和。
因为攻击范围是等腰直角三角形,所以上图中的大三角形也是等腰直角三角形。如果我们用蓝色梯形右上角的顶点来记蓝色梯形,那么蓝色梯形就是固定的,用红色梯形右上角的顶点来记红色梯形,那么红色梯形就是固定的。
上面,我们说了这道题实际是让我们求梯形,长方形和三角形的前缀和,但实际上我们只要记梯形和长方形的前缀和就可以了。在上图中,我们将红色梯形右上角的顶点沿大三角形的斜边往下移,都可以构成满足条件的图形,当移到蓝色梯形右下角时,就形成了一个三角形。
Part 2 实现
求长方形前缀和,可以用公式:
s u m [ i ] [ j ] = s u m [ i − 1 ] [ j ] + s u m [ i ] [ j − 1 ] − s u m [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j] sum[i][j]=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+a[i][j]
求梯形前缀和很难求,可以一层一层来求。先求一行的前几个的前缀和与一列的前几个的前缀和,再累加即可得到梯形前缀和。
Part 3 代码(C++)
#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,ans;
inline void work(){
ans=0;
scanf("%d%d%d",&n,&m,&k);
vector<string> str;
str.resize(n+1);
for(int i=1;i<=n;i++){
string a;
cin>>a;
str[i]=' '+a;
}
for(int dir=1;dir<=4;dir++){
vector<vector<int>> xd1(n+10,vector<int>(m+10,0)),xd2(n+10,vector<int>(m+10,0));
vector<vector<int>> tx1(n+10,vector<int>(m+10,0)),tx2(n+10,vector<int>(m+10,0));
vector<vector<int>> jx(n+10,vector<int>(m+10,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
xd1[i][j]=xd1[i][j-1];
xd2[i][j]=xd2[i-1][j];
if(str[i][j]=='#'){
xd1[i][j]++;
xd2[i][j]++;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
tx1[i][j]=tx1[i-1][min(m,j+1)]+xd1[i][j];
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
tx2[j][i]=tx2[min(n,j+1)][i-1]+xd2[j][i];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
jx[i][j]=jx[i-1][j]+jx[i][j-1]-jx[i-1][j-1];
if(str[i][j]=='#'){
jx[i][j]++;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int sum=i+j+k-1,x,y;
if(sum<=m){
x=1;
y=sum;
}else{
x=min(n,1+sum-m);
y=m;
}
ans=max(ans,tx2[x][y]-tx1[i-1][min(m,j+k+1)]-tx2[min(n,i+k+1)][j-1]+jx[i-1][j-1]);
}
}
swap(n,m);
vector<string> st(n+1,string(m+1,' '));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
st[i][j]=str[j][n-i+1];
}
}
str=st;
}
printf("%d\n",ans);
}
int main(){
scanf("%d",&t);
for(;t--;){
work();
}
}
时间复杂度为 O ( 24 N M ) O(24NM) O(24NM)