二进制迷宫 贪心 BFS

来源:互联网 发布:win10网络重置命令 编辑:程序博客网 时间:2024/05/22 15:07

搜索专项训练赛 二进制迷宫

题目大意

给出一个n*m的图,数字要么是0要么是1。从左上角坐标为(1,1)的格子出发,走到右下角的坐标为(n,m)的格子,可以沿上下左右四个方向行走。每到一个格子,就记录下里面的数字。到达终点的时候,将得到一个由0和1构成的序列,把这个序列看做一个二进制数(可以含前导零)。要求这个二进制数尽可能小,计算并输出这个二进制数。

数据范围

对于30%的数据:1≤n,m≤100
对于100%的数据:1≤n,m≤1000


考试的时候并没有说是搜索专项训练赛,所以一开始想到DP是很正常的,竟然有同学使用了string类型的DP数组,真是开了眼界(然而这样AC不了)。

首先容易想到,去掉前导零后,这个二进制数的位数要尽量少,在保证位数最少的情况下,尽量让高位选0。这个贪心是显然正确的,但是怎样知道位数最小值是多少?从哪里开始贪心?

题目中上下左右都可以行走,如果是DP的话状态不是很好转移。如果说只能向下或向右走容易让我们想到DP,那么多个方向都能行走容易让我们想到BFS

其实,“只能向下或向右走”也能指向正解。如果当前已经有1出现了,那么就只能向下或向右走了,因为不这样做会使位数增多。注意到在只能向下或向右走的前提下,走到终点的步数是一定的,是能够算出来的。保证位数最少,可以通过BFS找到离终点“最近”的一个或者多个0。

接下来就要从这些0出发找到最优解。只能向下或向右走,此时当然可以用DP了,但是用string类型的DP数组?其实还是BFS就可以了。开一个答案数组,记录答案的每一位上填0还是填1,这样就可以剪掉上一步不可能达到最优解的情况,具体实现见代码。

时间复杂度O(mn)


代码:

#include<stdio.h>#include<queue>#include<algorithm>#define MAXN 1005using namespace std;int N,M,id[MAXN][MAXN];char Map[MAXN][MAXN];int Ans[MAXN*2];bool vis[MAXN][MAXN];struct node{int x,y,step,v;};//x,y表示位置,step记录从符合要求的0开始已经走了多少步,v记录上一步的权值queue<node>Q;int Max=1;int dx[4]={1,0,0,-1},dy[4]={0,1,-1,0};void BFS(){    vis[1][1]=true;    if(Map[1][1]=='1')return;    node tmp,head;    tmp.x=1;tmp.y=1;    Q.push(tmp);    int k,a,b,ta,tb;    while(Q.size())    {        head=Q.front();Q.pop();        a=head.x;b=head.y;        for(k=0;k<4;k++)        {            ta=a+dx[k];tb=b+dy[k];            if(!vis[ta][tb]&&Map[ta][tb]=='0'&&ta&&tb&&ta<=N&&tb<=M)            {                vis[ta][tb]=true;                Max=max(Max,ta+tb-1);//找“最靠近终点”的0                tmp.x=ta;tmp.y=tb;                Q.push(tmp);            }        }    }}bool mark[MAXN][MAXN];void GetAns(){    int i,j,x,y,a,b,ta,tb,tv;    node tmp,head;    for(i=1;i<=N;i++)    {        x=i;y=Max-x+1;        if(vis[x][y]&&y>0&&x>0&&y<=M)        {            tmp.x=x;tmp.y=y;tmp.step=0;tmp.v=0;            Q.push(tmp);        }    }//多个满足条件的0,全部入队    while(Q.size())    {        head=Q.front();Q.pop();        if(head.v>Ans[head.step])continue;//剪掉不能得到最优解的情况        a=head.x;b=head.y;        for(i=0;i<2;i++)        {            ta=a+dx[i];tb=b+dy[i];            if(ta&&tb&&ta<=N&&tb<=M)            {                tv=Map[ta][tb]-'0';                if(Ans[head.step+1]>=tv)                {                    Ans[head.step+1]=tv;                    tmp.step=head.step+1;                    tmp.x=ta;tmp.y=tb;                    tmp.v=tv;                    if(!mark[ta][tb])Q.push(tmp),mark[ta][tb]=true;//防止同一个点多次进队                }            }        }    }}int main(){    int i,j,tot=0;    scanf("%d%d",&N,&M);    for(i=1;i<=N;i++)scanf("%s",&Map[i][1]);    BFS();    for(i=0;i<=N+M-Max;i++)Ans[i]=1e9;      GetAns();    if(Map[1][1]=='1')putchar('1');    for(i=1;i<=N+M-Max-1;i++)printf("%d",Ans[i]);    if(Map[1][1]=='0'&&N+M-Max-1==0)putchar('0');    //答案就是0,不能输出空串}