2017.2.09【初中部 提高组】模拟赛C组 城市统计 题解

原题:

http://172.16.0.132/junior/#contest/show/1373/2

题目描述:

中山市的地图是一个n*n的矩阵,其中标号为1的表示商业区,标号为0的表示居民区。为了考察市内居民区与商业区的距离,并对此作出评估,市长希望你能够编写一个程序完成这一任务。
居民区i到商业区的距离指的是到距离它最近的商业区j的距离(|Xi-Xj|+|Yi-Yj|),而你将统计的是对于城市中的每一个区域k,以它为中心,所有满足max(|Xk-Xm|,|Yk-Ym|)<=r的区域m到商业区距离之和。结果同样以n*n的矩阵形式输出。

输入:

第一行为n,r(1<=r

输出:

n*n的矩阵。

样例输入:

4 1
1 0 0 0
1 1 0 0
0 1 1 0
0 1 0 0

样例输出:

1 4 9 8
2 5 10 9
2 4 7 7
2 3 4 4

分析:

这是一道很简单的题目。
根据题意,阶梯可分为两个阶段,首先对于给出的01图,计算出个点到商业区的距离,接下来在进行求和。
计算距离可以从商业区开始逐渐推算,设一个对列,将所有的商业区放到队列里,同时将他们的距离标为0,然后从队列头开始,察看每一个结点的上下左右,如果有未标明距离的,就将其激励标为当前结点距离+1,然后把它将入队列,当队列里所有结点均已察看,所有点到商业区的距离就计算好了。
然后是求和,如果按照一般的方法,枚举每一个点,然后以它为中心,将一个正方形内的所有点加起来求和,当数据很大的时候就会比较慢(例如n=100,r=100,m=20的时候),因为是矩阵上的求和,所以我们很容易就想到用部分和来解决。
我们设第一步计算距离完毕后的结果为矩阵a,设f[i][j]表示从矩阵a左上角到(i,j)一个矩阵内所有点的和,那么若要求以(i,j)为中心,半径为r的正方形内所有点的和,很简单就可以得到一下的算式:
answer[i][j]=f[i+r][j+r]-f[i-r-1][j+r]-f[i+r][j-r-1]+f[i-r-1][j-r-1]
当然,加上r或减去r是会导致超出矩阵1..n的范围的,要注意处理一下以免越界。

实现:

uses math;
const
        m:array[1..4,1..2] of longint=((1,0),(0,1),(-1,0),(0,-1));
var
        d:array[1..100007,1..2] of longint;
        bz:array[0..150,0..150] of boolean;
        map,dis,f:array[0..150,0..150] of longint;
        t,ans,head,tail,xx,yy,n,r,x,y,i,j:longint;
procedure dfs;
begin
        head:=0;
        while head<>tail do
        begin
                head:=head mod 1000000+1;
                x:=d[head,1]; y:=d[head,2];
                for i:=1 to 4 do
                begin
                        xx:=x+m[i,1]; yy:=y+m[i,2];
                        if dis[xx,yy]>dis[x,y]+1 then
                        begin
                                dis[xx,yy]:=dis[x,y]+1;
                                if not bz[xx,yy] then
                                begin
                                        tail:=tail mod 1000000+1;
                                        d[tail,1]:=xx; d[tail,2]:=yy;
                                        bz[xx,yy]:=true;
                                end;
                        end;
                end;
                bz[x,y]:=false;
        end;
        for i:=1 to n do
                for j:=1 to n do f[i,j]:=f[i-1,j]+f[i,j-1]-f[i-1,j-1]+dis[i,j];
end;
begin
        assign(input,'city.in');reset(input);
        assign(output,'city.out');rewrite(output);
        readln(t);
        while t>0 do
        begin
                fillchar(bz,sizeof(bz),false);
                fillchar(f,sizeof(f),0);
                tail:=0;
                head:=0;
                readln(n,r);
                for i:=1 to n do
                        for j:=1 to n do dis[i,j]:=maxlongint div 2;
                for i:=1 to n do
                        for j:=1 to n do
                        begin
                                read(map[i,j]);
                                if map[i,j]=1 then
                                begin
                                        inc(tail);
                                        d[tail,1]:=i; d[tail,2]:=j;
                                        dis[i,j]:=0;
                                        bz[i,j]:=true;
                                end;
                        end;
                dfs;
                for i:=1 to n do
                begin
                        for j:=1 to n do
                        begin
                                x:=min(i+r,n); y:=min(j+r,n);
                                xx:=max(0,i-r-1);yy:=max(0,j-r-1);
                                ans:=f[x,y]-f[xx,y]-f[x,yy]+f[xx,yy];
                                write(ans,' ');
                        end;
                        writeln;
                end;
                writeln;
                dec(t);
        end;
        close(input);close(output);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值