【BFS】【多元最短路】问题 D: 显示图像

问题 D: 显示图像
时间限制: 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。

一开始想的是暴力做法,让每个黑点与每个白点去求距离,大部分能过,但是部分案例会TLE

#include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep1(i,n) for(int i=1;i<=(int)n;i++)
#define redp(i,n) for(int i=(int)n-1;i>=0;i--)
#define redp1(i,n) for(int i=(int)n;i>=1;i--)
#define lowbit(x) (x)&(-x)
#define EPSILON 1e-9
typedef long long ll;
using namespace std;
const int dx[] = { 0, 1, -1, 0, 1, -1, 1, -1 };
const int dy[] = { 1, 0, 0, -1, 1, -1, -1, 1 };
const int M = 1e9 + 7;
const int INF=0x3f3f3f3f;
//const double Pi=acos(-1);
const int MOD = 998244353;
bool flag=false;
//----------------------------------------------
const int N=2e6+10;
//----------------------------------------------
int n,m;
int e=0;
char a[1005][1005];
int b[1005][1005];
struct node
{
    int x,y;
} bai[100005];
void fun(int ii,int jj)
{
    for(int i=1;i<=e;i++)b[ii][jj]=min(b[ii][jj],abs(ii-bai[i].x)+abs(jj-bai[i].y));
}
//----------------------------------------------
int main()
{
    int i,j,k,l;
    memset(b,0x3f,sizeof(b));
    cin>>n>>m;
    rep1(i,n)rep1(j,m)
    {
        cin>>a[i][j];
        if(a[i][j]=='1')
        {
            e++;
            bai[e].x=i;
            bai[e].y=j;

            b[i][j]=0;
        }
    }
    rep1(i,n)rep1(j,m)
    {
        if(a[i][j]=='0')fun(i,j);
    }
    rep1(i,n)
    {
        rep1(j,m)
        {
            cout<<b[i][j]<<" ";;
        }
        cout<<endl;
    }
    return 0;
}

正确思路 用a数组先把黑点初始为一个较大的值,白点为0,q数组记录下白点坐标,然后让白点去往四个方向做bfs,当方案更优时更新黑点数值,同时q数组往后加入已更新的黑点坐标,当白点枚举完后枚举更新后的黑点,用黑点坐标去更新黑点坐标,每个黑点会多次更新,最终得最优解。

#include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep1(i,n) for(int i=1;i<=(int)n;i++)
#define redp(i,n) for(int i=(int)n-1;i>=0;i--)
#define redp1(i,n) for(int i=(int)n;i>=1;i--)
#define lowbit(x) (x)&(-x)
#define EPSILON 1e-9
typedef long long ll;
using namespace std;
const int dx[] = { 0, 1, -1, 0, 1, -1, 1, -1 };
const int dy[] = { 1, 0, 0, -1, 1, -1, -1, 1 };
const int M = 1e9 + 7;
const int INF=0x3f3f3f3f;
//const double Pi=acos(-1);
const int MOD = 998244353;


//----------------------------------------------
const int N=2e6+10;
//----------------------------------------------
int n,m;
int a[190][190];          //答案数组
bool flag[190][190];    //标记数组
int cnt=0;

struct node
{
    int x,y;
} q[183*183];      //先存白点坐标数组

void show()
{
    rep1(i,n)
    {
        rep1(j,m)printf("%d ",a[i][j]);

        printf("\n");
    }
    printf("\n");
}

void bfs()
{

    int k=1;
    int t=cnt; //

    while(k<=t)
    {
        rep(i,4)   //方向数组前四个刚好是上下左右四个方向,我懒(不是
        {
            int tx=q[k].x+dx[i];
            int ty=q[k].y+dy[i];
            if(tx>=1&&tx<=n&&ty>=1&&ty<=m)
            {
                if(!flag[tx][ty]&&a[q[k].x][q[k].y]+1<a[tx][ty])   //当发现更优答案时
                {

                    t++; q[t].x=tx; q[t].y=ty;  //用q数组在白点后存储黑点坐标

                    a[tx][ty]=a[q[k].x][q[k].y]+1;  //更新
                    //show();
                }
            }
        }
        k++;
    }
}
//----------------------------------------------
int main()
{

    int i,j,k,l;
    char ch;
    cin>>n>>m;
    rep1(i,n)rep1(j,m)
    {
        cin>>ch;

        if(ch=='1')
        {
            cnt++;        //记录白点个数
            q[cnt].x=i;      //记录白点坐标
            q[cnt].y=j;
            flag[i][j]=true;   //白点置true
            a[i][j]=0;          //白点赋0
        }
        else if(ch=='0')flag[i][j]=false,a[i][j]=0x7ff;    //黑点置false 且赋一个较大的值

    }

    bfs();

    rep1(i,n)
    {
        rep1(j,m)cout<<a[i][j]<<" ";
        cout<<endl;
    }


    return 0;
}

这里贴一下用show打印的每次更新后的答案数组,方便理解。

2047 2047 2047 0
2047 2047 0 0
2047 0 0 2047

2047 2047 1 0
2047 2047 0 0
2047 0 0 2047

2047 2047 1 0
2047 1 0 0
2047 0 0 2047

2047 2047 1 0
2047 1 0 0
2047 0 0 1

2047 2047 1 0
2047 1 0 0
1 0 0 1

2047 2 1 0
2047 1 0 0
1 0 0 1

2047 2 1 0
2 1 0 0
1 0 0 1

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

3 2 1 0
2 1 0 0
1 0 0 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值