[双端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;
}