正题
大意
有n*n的矩阵,有居民区有商业区,每个居民区曼哈顿距离最近的商业区的曼哈顿距离就是那个居民区离商业区的距离。每个格子统计一下以它为中心的2∗r+1×2∗r+12∗r+1×2∗r+1的矩阵内所有居民区离商业区的距离的和。
解题思路
先bfs计算所有居民离商业区的距离,然后用二维前缀和O(n2)O(n2)处理。
关于二维前缀和
二维前缀和就是用a[i][j]a[i][j]表示(1,1)(1,1)到(i,j)(i,j)这个矩阵范围内的和,第一行和第一列可以直接像一维那样推,然后我们假设我们已经推出了a[i−1][j]a[i−1][j]和a[i][j−1]a[i][j−1]和a[i−1][j−1]a[i−1][j−1],然后我们可以看一下如果计算
我们用a[i−1][j]a[i−1][j]加上a[i][j−1]a[i][j−1],这样的话蓝色区域和红色区域被计算过一次,可是紫色区域被累加过两次,所以我们要减去一次紫色区间a[i−1][j−1]a[i−1][j−1]。然后在加上白色区间f[i][j]f[i][j]。
计算答案也利用容斥定理来计算。
代码
#include<cstdio>
#include<queue>
#include<cstring>
#define N 151
using namespace std;
queue<int> x,y;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int n,r,f[N][N],ans[N][N],t;
int c[N][N];
void bfs()//广搜
{while (!x.empty()){for (int i=0;i<4;i++){int zx=x.front()+dx[i],zy=y.front()+dy[i];if (zx<1||zx>n||zy<1||zy>n||c[zx][zy]) continue;c[zx][zy]=true;f[zx][zy]=f[x.front()][y.front()]+1;x.push(zx);y.push(zy);//printf("%d %d\n",zx,zy);}x.pop();y.pop();}
}
int main()
{scanf("%d",&t);while (t--){memset(f,0,sizeof(f));memset(ans,0,sizeof(ans));scanf("%d%d",&n,&r);for (int i=1;i<=n;i++)for (int j=1;j<=n;j++){scanf("%d",&c[i][j]);if (c[i][j]) x.push(i),y.push(j);}bfs();for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]+f[i][j];//计算前缀和for (int i=1;i<=n;i++){for (int j=1;j<=n;j++){int wx=min(n,i+r),wy=min(n,j+r),lx=max(0,i-r-1),ly=max(0,j-r-1);//计算区域以免爆出范围printf("%d ",ans[wx][wy]+ans[lx][ly]-ans[lx][wy]-ans[wx][ly]);//输出答案}printf("\n");}printf("\n");}
}