[双端BFS][二维单调队列] 2020科大讯飞杯上海大学友谊赛 K.迷宫

题目链接

Solution

传送操作最多使用一次,所以可以从起点和终点分别开始BFS,然后枚举点对进行传送。因为可以传送的点对要满足切比雪夫距离小于等于d,实际上就是一个\((d+1)\times (d+1)\)的子矩阵内的点对可以互相传送,这个东西可以用二维单调队列来维护。找到使得路程最小的传送的两个点后,输出路径即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;

#define RG register int
#define LL long long

struct node{int x,y;};
struct Node{int x,y,Step;};
queue<Node> Q;
Node DataA[2005][2005],DataB[2005][2005];
char G[2005][2005];
int visA[2005][2005],visB[2005][2005];
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
vector<node> RouteA,RouteB;
int N,M,T,Stx,Sty,Edx,Edy,Ax,Ay,Bx,By,Ans=2147483647;

void BFSA(){
    memset(visA,0x3f,sizeof(visA));
    while(!Q.empty()) Q.pop();
    Q.push((Node){Stx,Sty,0});visA[Stx][Sty]=0;
    DataA[Stx][Sty]=(Node){Stx,Sty,0};
    while(!Q.empty()){
        Node Cur=Q.front();Q.pop();
        for(RG i=0;i<4;++i){
            int Nx=Cur.x+dx[i],Ny=Cur.y+dy[i];
            if(G[Nx][Ny]=='X') continue;
            if(visA[Nx][Ny]<100000000) continue;
            if(Nx<1||Nx>N||Ny<1||Ny>M) continue;
            visA[Nx][Ny]=Cur.Step+1;
            Q.push((Node){Nx,Ny,Cur.Step+1});
            DataA[Nx][Ny]=Cur;
        }
    }
    return;
}

void BFSB(){
    memset(visB,0x3f,sizeof(visB));
    while(!Q.empty()) Q.pop();
    Q.push((Node){Edx,Edy,0});visB[Edx][Edy]=0;
    DataB[Edx][Edy]=(Node){Edx,Edy,0};
    while(!Q.empty()){
        Node Cur=Q.front();Q.pop();
        for(RG i=0;i<4;++i){
            int Nx=Cur.x+dx[i],Ny=Cur.y+dy[i];
            if(G[Nx][Ny]=='X') continue;
            if(visB[Nx][Ny]<100000000) continue;
            if(Nx<1||Nx>N||Ny<1||Ny>M) continue;
            visB[Nx][Ny]=Cur.Step+1;
            Q.push((Node){Nx,Ny,Cur.Step+1});
            DataB[Nx][Ny]=Cur;
        }
    }
    return;
}

template<typename elemType,typename CMP>
struct MPQueue{
    CMP cmp;
    int Pos[2010];
    elemType Node[2010];
    int head,tail,limit;//limit-滑动窗口长度
    MPQueue():head(1),tail(0),limit(1){}
    MPQueue(int _limit):head(1),tail(0),limit(_limit){}
    
    bool size(){return tail-head+1;}
    bool empty(){return tail<head;}//是否为空
    elemType front_elem(){return Node[head];}//获取队首的元素
    int front_pos(){return Pos[head];}//获取队首元素在原序列中的位置
    //以上操作使用前应保证先pop_front队首的过时元素

    void set_limit(int _limit){limit=_limit;}//设置滑动窗口长度
    void clear(){head=1,tail=0;}//清空单调队列
    void push(int pos,elemType elem){//插入元素,维护最小值
        //pos-元素在原序列中的位置,elem-要插入的元素,limit-滑动窗口的长度
        while(head<=tail && (!cmp(Node[tail],elem) || pos-Pos[tail]+1>limit))
            --tail;
        ++tail;Node[tail]=elem;Pos[tail]=pos;
    }
    void pop_front(int pos){//处理过时的队首元素
        while(head<=tail && pos-Pos[head]>=limit) ++head;
    }
};

template<typename elemType,typename CMP>
struct MPQueue2D{//二维单调队列
    struct NODE{int posy;elemType Value;};
    struct CMP2{bool operator()(const NODE &A,const NODE &B)const {
        return CMP()(A.Value,B.Value);}
    };
    MPQueue<elemType,CMP> Row[2010];
    MPQueue<NODE,CMP2> Col;
    int x[2010][2010],y[2010][2010];//x[i][j]-以(i,j)为右下角的子矩阵中最值的行号
                                    //y[i][j]-以(i,j)为右下角的子矩阵中最值的列号
    elemType val[2010][2010],Data[2010][2010];
    //val[i][j]-以(i,j)为右下角的子矩阵中的最值
    //Data-二维单调队列的数据源
    int N,M,limitR,limitC;
    //N-行数,M-列数,limitR-滑动的子矩阵的行数,limitC-滑动的子矩阵的列数
    
    MPQueue2D(int _N=0,int _M=0,int _limitR=0,int _limitC=0):
        N(_N),M(_M),limitR(_limitR),limitC(_limitC) {}
    void setN(int _N){N=_N;}
    void setM(int _M){M=_M;}
    void clear(){
        for(RG i=1;i<=N;++i)
            Row[i].clear();
        Col.clear();
    }
    void set_limit(int _limitR,int _limitC){//设置滑动子矩阵的行数limitR和列数limitC
        limitR=min(_limitR,N);limitC=min(_limitC,M);
        for(RG i=1;i<=N;++i)
            Row[i].set_limit(limitC);
        Col.set_limit(limitR);
    }
    void Query(){//查询二维数组中每个滑动子矩阵的最值及其位置
        for(RG j=1;j<=M;++j){
            Col.clear();//维护列的单调队列
            for(RG i=1;i<=N;++i){
                Row[i].push(j,Data[i][j]);
                Row[i].pop_front(j);
                Col.push(i,(NODE){Row[i].front_pos(),Row[i].front_elem()});
                Col.pop_front(i);
                x[i][j]=Col.front_pos();//以(i,j)为右下角的子矩阵中最值的行号
                y[i][j]=Col.front_elem().posy;//以(i,j)为右下角的子矩阵中最值的列号
                val[i][j]=Col.front_elem().Value;//以(i,j)为右下角的子矩阵中的最值
            }
        }
    }
};

MPQueue2D<int,less<int> > Q1,Q2;

inline void Solve(){
    int px=Ax,py=Ay;
    RouteA.push_back((node){px,py});
    while(!(px==Stx && py==Sty)){
        int x=DataA[px][py].x,y=DataA[px][py].y;
        px=x;py=y;RouteA.push_back((node){px,py});
    }
    px=Bx,py=By;
    if(!(Bx==Ax && By==Ay))
        RouteB.push_back((node){px,py});
    
    while(!(px==Edx && py==Edy)){
        int x=DataB[px][py].x,y=DataB[px][py].y;
        px=x;py=y;RouteB.push_back((node){px,py});
    }
    int Res=RouteA.size()+RouteB.size()-1;
    printf("%d\n",Res);
    for(RG i=RouteA.size()-1;i>=0;--i)
        printf("%d %d\n",RouteA[i].x-1,RouteA[i].y-1);
    for(RG i=0;i<RouteB.size();++i)
        printf("%d %d\n",RouteB[i].x-1,RouteB[i].y-1);
    return;
}

int main(){
    scanf("%d%d%d",&N,&M,&T);
    for(RG i=1;i<=N;++i){
        scanf("%s",G[i]+1);
        for(RG j=1;j<=M;++j){
            if(G[i][j]=='S'){Stx=i;Sty=j;}
            if(G[i][j]=='T'){Edx=i;Edy=j;}
        }
    }
    if(max(abs(Edx-Stx),abs(Edy-Sty))<=T){
        printf("1\n");
        printf("%d %d\n%d %d\n",Stx-1,Sty-1,Edx-1,Edy-1);
        return 0;
    }
    BFSA();BFSB();
    Q1.N=Q2.N=N;Q1.M=Q2.M=M;
    Q1.set_limit(T+1,T+1);Q2.set_limit(T+1,T+1);
    for(RG i=1;i<=N;++i){
        for(RG j=1;j<=M;++j){
            Q1.Data[i][j]=visA[i][j];
            Q2.Data[i][j]=visB[i][j];
        }
    }
    Q1.Query();Q2.Query();
    int LenA=0,LenB=0;
    for(RG i=1;i<=N;++i){
        for(RG j=1;j<=M;++j){
            if(Q1.val[i][j]+Q2.val[i][j]+1<Ans){
                Ans=Q1.val[i][j]+Q2.val[i][j]+1;
                Ax=Q1.x[i][j];Ay=Q1.y[i][j];
                Bx=Q2.x[i][j];By=Q2.y[i][j];
                LenA=Q1.val[i][j];LenB=Q2.val[i][j];
            }
        }
    }
    if(Ans>10000000){printf("-1\n");return 0;}
    RouteA.reserve(LenA+5);
    RouteB.reserve(LenB+5);
    Solve();
    return 0;
}
posted @ 2020-04-19 14:32  AE酱  阅读(143)  评论(0编辑  收藏  举报