jzoj2137-(GDKOI2004)城市统计【二维前缀和,bfs】

正题


大意

有n*n的矩阵,有居民区有商业区,每个居民区曼哈顿距离最近的商业区的曼哈顿距离就是那个居民区离商业区的距离。每个格子统计一下以它为中心的 2 r + 1 × 2 r + 1 的矩阵内所有居民区离商业区的距离的和。


解题思路

先bfs计算所有居民离商业区的距离,然后用二维前缀和 O ( n 2 ) 处理。

关于二维前缀和

二维前缀和就是用 a [ i ] [ j ] 表示 ( 1 , 1 ) ( i , j ) 这个矩阵范围内的和,第一行和第一列可以直接像一维那样推,然后我们假设我们已经推出了 a [ i 1 ] [ j ] a [ i ] [ j 1 ] a [ i 1 ] [ j 1 ] ,然后我们可以看一下如果计算
这里写图片描述
我们用 a [ i 1 ] [ j ] 加上 a [ i ] [ j 1 ] ,这样的话蓝色区域和红色区域被计算过一次,可是紫色区域被累加过两次,所以我们要减去一次紫色区间 a [ i 1 ] [ j 1 ] 。然后在加上白色区间 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");
    }
}

猜你喜欢

转载自blog.csdn.net/mr_wuyongcong/article/details/81063210