bfs+queue求解曼哈顿距离

给定 ( x 1 , y 1 ) \left(x_{1}, y_{1}\right) (x1,y1) ( x 2 , y 2 ) \left(x_{2}, y_{2}\right) (x2,y2),两点间的曼哈顿距离为
d 12 = ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ d_{12}=\left|x_{1}-x_{2}\right|+\left|y_{1}-y_{2}\right| d12=x1x2+y1y2

Q1:显示图像

时间限制: 1 Sec 内存限制: 128 MB
题目描述
古老的显示屏是由N×M个象素(Pixel)点组成的。一个象素点的位置是根据所在行数和列数决定的。例如P(2,1)表示第2行第1列的象素点。那时候,屏幕只能显示黑与白两种颜色,人们用二进制0和1来表示。0表示黑色,1表示白色。当计算机发出一个指令:P(x,y)=1,则屏幕上的第x行第y列的阴极射线管就开始工作,使该象素点显示白色,若P(x,y)=0,则对应位置的阴极射线管不工作,象素点保持黑色。在某一单位时刻,计算机以N×M二维01矩阵的方式发出显示整个屏幕图像的命令。
例如,屏幕是由3×4象素点组成,在某单位时刻,计算机发出如下命令:
0001
0011
0110
则屏幕图像为:
(假设放大后,一个格子表示一个象素点)
在这里插入图片描述
由于未知的原因,显示黑色的象素点总是受显示白色的象素点的影响——可能是阴极射线管工作的作用。并且,距离越近,影响越大。这里的距离定义如下:设有象素点P1(x1,y1)和象素点P2(x2,y2),则它们之间的距离D(P1,P2):D(P1,P2)=|x1-x2|+|y1-y2|
在某一时刻,计算机发出显示命令后,科学家们期望知道,每个象素点和其最近的显示白色的象素点之间的最短距离是多少——科学家们保证屏幕上至少有一个显示白色的象素点。
上面的例子中,象素P(1,1)与最近的白色象素点之间的距离为3,而象素P(3,2)本身显示白色,所以最短距离为0。

输入
第一行有两个数字,N和M (1<=N,M<=1000),表示屏幕的规格。
以下N行,每行M个数字,0或1。为计算机发出的显示命令。

输出
输出有N行,每行M个数字,中间用1个空格分开。第i行第j列的数字表示距象素点P(i,j)最近的白色象素点的最短距离。
样例输入 Copy

3 4
0001
0011
0110

样例输出 Copy

3 2 1 0
2 1 0 0
1 0 0 1

提示
对于100%的数据:N*M<=182^2。
考查bfs+queue。
题意即求每个像素距白色像素的最短距离,考虑广度优先搜索,从每个白色像素开始上、下、左、右四个方向搜索,维护一个队列。注意要对每个白色像素进行广度优先搜索,所有的白色像素搜索完后得到的距离即最小距离。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
char a[1005][1005];
int scr[1005][1005];
const int inf=0x3f3f3f3f;
int n,m;
typedef struct node
{
    
    
    int x,y,step;
}Node;
void bfs(int x,int y)
{
    
    
    queue<Node>q;
    Node start,t,t1,t2,t3,t4;
    start.step=0;start.x=x;start.y=y;
    q.push(start);
    while(!q.empty())//维护一个队列,先访问四个方向上的点,然后判断是否符合要求(不越界、是黑色像素、当前距离+1小于要访问的点的距离),如果符合要求就记录最小距离并入队。
    {
    
    
        t=q.front();
        q.pop();
        t1.x=t.x+1;t1.y=t.y;
        t2.x=t.x-1;t2.y=t.y;
        t3.x=t.x;t3.y=t.y+1;
        t4.x=t.x;t4.y=t.y-1;
        if(1<=t1.x&&t1.x<=n&&1<=t1.y&&t1.y<=m&&a[t1.x][t1.y]=='0'&&scr[t1.x][t1.y]>scr[t.x][t.y]+1)
        {
    
    
            scr[t1.x][t1.y]=scr[t.x][t.y]+1;
            t1.step=t.step+1;
            q.push(t1);
        }
        if(1<=t2.x&&t2.x<=n&&1<=t2.y&&t2.y<=m&&a[t2.x][t2.y]=='0'&&scr[t2.x][t2.y]>scr[t.x][t.y]+1)
        {
    
    
            scr[t2.x][t2.y]=scr[t.x][t.y]+1;
            t2.step=t.step+1;
            q.push(t2);
        }
        if(1<=t3.x&&t3.x<=n&&1<=t3.y&&t3.y<=m&&a[t3.x][t3.y]=='0'&&scr[t3.x][t3.y]>scr[t.x][t.y]+1)
        {
    
    
            scr[t3.x][t3.y]=scr[t.x][t.y]+1;
            t3.step=t.step+1;
            q.push(t3);
        }
        if(1<=t4.x&&t4.x<=n&&1<=t4.y&&t4.y<=m&&a[t4.x][t4.y]=='0'&&scr[t4.x][t4.y]>scr[t.x][t.y]+1)
        {
    
    
            scr[t4.x][t4.y]=scr[t.x][t.y]+1;
            t4.step=t.step+1;
            q.push(t4);
        }
    }
}
int main()
{
    
    
    int i,j;
    for(i=0;i<=1001;i++)
    {
    
    
        for(j=0;j<=1001;j++)
        {
    
    
            scr[i][j]=inf;//求最小值,可以把点初始化为inf(0x3f3f3f3f)
        }
    }
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
    {
    
    
        scanf("%s",a[i]+1);
    }
    for(i=1;i<=n;i++)
    {
    
    
        for(j=1;j<=m;j++)
        {
    
    
            if(a[i][j]=='1')
            {
    
    
                scr[i][j]=0;
                bfs(i,j);//对每个白色像素点进行广度优先搜索
            }
        }
    }
    for(i=1;i<=n;i++)
    {
    
    
        for(j=1;j<=m;j++)
        {
    
    
            printf("%d",scr[i][j]);
            if(j<m)printf(" ");
        }
        if(i<n)printf("\n");
    }
    return 0;
}
/**************************************************************
    Language: C++
    Result: 正确
    Time:142 ms
    Memory:6876 kb
****************************************************************/

Q2:血色先锋队

时间限制: 1 Sec 内存限制: 128 MB
题目描述
巫妖王的天灾军团终于卷土重来,血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团,以及一切沾有亡灵气息的生物。孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重包围,现在他们将主力只好聚集了起来,以抵抗天灾军团的围剿。可怕的是,他们之中有人感染上了亡灵瘟疫,如果不设法阻止瘟疫的扩散,很快就会遭到灭顶之灾。大领主阿比迪斯已经开始调查瘟疫的源头。原来是血色先锋军的内部出现了叛徒,这个叛徒已经投靠了天灾军团,想要将整个血色先锋军全部转化为天灾军团!无需惊讶,你就是那个叛徒。在你的行踪败露之前,要尽快完成巫妖王交给你的任务。

军团是一个N行M列的矩阵,每个单元是一个血色先锋军的成员。感染瘟疫的人,每过一个小时,就会向四周扩散瘟疫,直到所有人全部感染上瘟疫。你已经掌握了感染源的位置,任务是算出血色先锋军的领主们感染瘟疫的时间,并且将它报告给巫妖王,以便对血色先锋军进行一轮有针对性的围剿。

输入
第1行:四个整数N,M,A,B,表示军团矩阵有N行M列。有A个感染源,B为血色敢死队中领主的数量。
接下来A行:每行有两个整数x,y,表示感染源在第x行第y列。
接下来B行:每行有两个整数x,y,表示领主的位置在第x行第y列。

输出
第1至B行:每行一个整数,表示这个领主感染瘟疫的时间,输出顺序与输入顺序一致。如果某个人的位置在感染源,那么他感染瘟疫的时间为0。
样例输入 Copy

5 4 2 3
1 1
5 4
3 3
5 3
2 4

样例输出 Copy

扫描二维码关注公众号,回复: 11710914 查看本文章
3
1
3

提示
如下图,标记出了所有人感染瘟疫的时间以及感染源和领主的位置。
在这里插入图片描述
【数据规模】
10%数据:N,M<=10
40%数据:N,M<=100
100%数据:1<=M,N<=500,1<=A,B<=M*N
这个题和上一个题思路基本一样,都考查bfs+queue。但是这个题的m*n最大为250000,再加上外层嵌套循环的时间复杂度为O(mn),如果采用上一题的做法会超时。

#include<cstdio>
#include<queue>
using namespace std;
int n,m,a,b,x,y;
int dis[505][505];
const int inf=0x3f3f3f3f;
typedef struct node
{
    
    
    int x,y,step;
}Node;
void bfs(int xx,int yy)
{
    
    
    queue<Node>q;
    Node start,t,t1,t2,t3,t4;
    start.x=xx;start.y=yy;start.step=0;
    q.push(start);
    while(!q.empty())
    {
    
    
        t=q.front();
        q.pop();
        t1.x=t.x+1;t1.y=t.y;
        t2.x=t.x-1;t2.y=t.y;
        t3.x=t.x;t3.y=t.y+1;
        t4.x=t.x;t4.y=t.y-1;
        if(1<=t1.x&&t1.x<=n&&1<=t1.y&&t1.y<=m&&dis[t1.x][t1.y]!=0&&dis[t.x][t.y]+1<dis[t1.x][t1.y])
        {
    
    
            dis[t1.x][t1.y]=dis[t.x][t.y]+1;
            t1.step=t.step+1;
            q.push(t1);
        }
        if(1<=t2.x&&t2.x<=n&&1<=t2.y&&t2.y<=m&&dis[t2.x][t2.y]!=0&&dis[t.x][t.y]+1<dis[t2.x][t2.y])
        {
    
    
            dis[t2.x][t2.y]=dis[t.x][t.y]+1;
            t2.step=t.step+1;
            q.push(t2);
        }
        if(1<=t3.x&&t3.x<=n&&1<=t3.y&&t3.y<=m&&dis[t3.x][t3.y]!=0&&dis[t.x][t.y]+1<dis[t3.x][t3.y])
        {
    
    
            dis[t3.x][t3.y]=dis[t.x][t.y]+1;
            t3.step=t.step+1;
            q.push(t3);
        }
        if(1<=t4.x&&t4.y<=n&&1<=t4.y&&t4.y<=m&&dis[t4.x][t4.y]!=0&&dis[t.x][t.y]+1<dis[t4.x][t4.y])
        {
    
    
            dis[t4.x][t4.y]=dis[t.x][t.y]+1;
            t4.step=t.step+1;
            q.push(t4);
        }
    }
}
int main()
{
    
    
    int i,j;
    for(i=1;i<=500;i++)
    {
    
    
        for(j=1;j<=500;j++)
        {
    
    
            dis[i][j]=inf;
        }
    }
    scanf("%d %d %d %d",&n,&m,&a,&b);
    while(a--)
    {
    
    
        scanf("%d %d",&x,&y);
        dis[x][y]=0;
    }
    for(i=1;i<=n;i++)
    {
    
    
        for(j=1;j<=m;j++)
        {
    
    
            if(dis[i][j]==0)bfs(i,j);
            //对每个距离为0(表示传染源)的点进行广度优先搜索,结果tle
        }
    }
    while(b--)
    {
    
    
        scanf("%d %d",&x,&y);
        printf("%d",dis[x][y]);
        if(b)printf("\n");
    }
    return 0;
}
/**************************************************************
    Language: C++
    Result: 时间超限
****************************************************************/

为了避免外层嵌套循环的影响,可以在标记传染源的同时就将该点入队,这样标记完传染源后队列中得到的是所有传染源对应的点,然后维护队列,完成bfs。

#include<cstdio>
#include<queue>
using namespace std;
int n,m,a,b,x,y;
int dis[505][505];
const int inf=0x3f3f3f3f;
typedef struct node
{
    
    
    int x,y,step;
}Node;
queue<Node>q;
int main()
{
    
    
    int i,j;
    Node t,t1,t2,t3,t4;
    for(i=1;i<=500;i++)
    {
    
    
        for(j=1;j<=500;j++)
        {
    
    
            dis[i][j]=inf;
        }
    }
    scanf("%d %d %d %d",&n,&m,&a,&b);
    while(a--)
    {
    
    
        scanf("%d %d",&x,&y);
        dis[x][y]=0;
        t.x=x;t.y=y;t.step=0;
        q.push(t);//标记传染源的同时将其入队
    }
    while(!q.empty())
    {
    
    
        t=q.front();
        q.pop();
        t1.x=t.x+1;t1.y=t.y;
        t2.x=t.x-1;t2.y=t.y;
        t3.x=t.x;t3.y=t.y+1;
        t4.x=t.x;t4.y=t.y-1;
        if(1<=t1.x&&t1.x<=n&&1<=t1.y&&t1.y<=m&&dis[t1.x][t1.y]!=0&&dis[t.x][t.y]+1<dis[t1.x][t1.y])
        {
    
    
            dis[t1.x][t1.y]=dis[t.x][t.y]+1;
            t1.step=t.step+1;
            q.push(t1);
        }
        if(1<=t2.x&&t2.x<=n&&1<=t2.y&&t2.y<=m&&dis[t2.x][t2.y]!=0&&dis[t.x][t.y]+1<dis[t2.x][t2.y])
        {
    
    
            dis[t2.x][t2.y]=dis[t.x][t.y]+1;
            t2.step=t.step+1;
            q.push(t2);
        }
        if(1<=t3.x&&t3.x<=n&&1<=t3.y&&t3.y<=m&&dis[t3.x][t3.y]!=0&&dis[t.x][t.y]+1<dis[t3.x][t3.y])
        {
    
    
            dis[t3.x][t3.y]=dis[t.x][t.y]+1;
            t3.step=t.step+1;
            q.push(t3);
        }
        if(1<=t4.x&&t4.y<=n&&1<=t4.y&&t4.y<=m&&dis[t4.x][t4.y]!=0&&dis[t.x][t.y]+1<dis[t4.x][t4.y])
        {
    
    
            dis[t4.x][t4.y]=dis[t.x][t.y]+1;
            t4.step=t.step+1;
            q.push(t4);
        }
    }
    while(b--)
    {
    
    
        scanf("%d %d",&x,&y);
        printf("%d",dis[x][y]);
        if(b)printf("\n");
    }
    return 0;
}
/**************************************************************
    Language: C++
    Result: 正确
    Time:74 ms
    Memory:4656 kb
****************************************************************/

猜你喜欢

转载自blog.csdn.net/upc122/article/details/106495152
今日推荐