Nowcodercontest5278 K 迷宫
可以说看起来不难写起来真的不简单,还得封装一下
\(O(n^2m^2)\):
\(dp[i][j][k]\)定义表示当前位置\((i,j)\),\(k\)是否用过传送,枚举\(O(d^2)\)转移,用\(\text{BFS}\)就能满足转移顺序
\(O(nm)\):
可以看到有三种转移形式
对于\(k=0,k=1\)的两种\(dp\)状态之间用\(\text{BFS}\)转移
\(k=0\rightarrow k=1\)的状态转移
这三种转移形式我们把它们分开三部分,对于\(k=0\rightarrow k=0\)的转移,每次取到的区域是一个矩形
定义辅助数组\(A[i][j]\)表示第\(i\)行,范围在\([j-2d,j]\)范围内的\(dp[i][j][0]\)的最小值和出现位置
固定\(i\),然后\(j\)从小到大枚举,是一个标准的单调队列模板
然后固定\(j\),从小到大枚举\(i\),找到\(k\in[i-d,i+d]\)最小的\(A[k][j+d]\)即可
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<cmath>
#include<cassert>
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
#define pb push_back
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
char IO;
template<class T=int> T rd(){
T s=0;
int f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e3+10,INF=1e9+10;
int n,m,d,sx,sy,fx,fy;
char s[N][N];
pair <int,int> pre1[N][N],pre2[N][N],pre3[N][N]; // 记录了转移方案
int dis[N][N];
queue <pair <int,int> > que; // bfs序列
const int z[5][4]={{-1,0},{1,0},{0,1},{0,-1}};
struct Node{
int x,y,d;
bool operator < (const Node __) const {
return d<__.d;
}
bool operator > (const Node __) const {
return d>__.d;
}
}A[N][N<<1],B[N*N]; //x,y决策位置,d为决策权值
int cnt;
Node Q[N];
int L,R;
int ansx[N*N*2],ansy[N*N*2],ansc;
int main(){
n=rd(),m=rd(),d=rd();
rep(i,1,n) scanf("%s",s[i]+1);
rep(i,1,n) rep(j,1,m) if(s[i][j]=='S') sx=i,sy=j;
rep(i,1,n) rep(j,1,m) if(s[i][j]=='T') fx=i,fy=j;
//三步转移
//Part1
rep(i,1,n) rep(j,1,m) dis[i][j]=INF;
que.push(make_pair(sx,sy)),dis[sx][sy]=0;
while(!que.empty()) {
int x=que.front().first,y=que.front().second; que.pop();
rep(i,0,3) {
int x1=x+z[i][0],y1=y+z[i][1];
if(dis[x1][y1]==INF && s[x1][y1]!='X') dis[x1][y1]=dis[x][y]+1,pre1[x1][y1]=make_pair(x,y),que.push(make_pair(x1,y1));
}
}
//Part2
rep(i,1,n) {
L=1,R=0;
rep(j,1,m+d) {
while(L<=R && Q[L].y<j-d*2) ++L;
if(dis[i][j]<INF && j<=m) {
Node t=(Node){i,j,dis[i][j]};
while(L<=R && Q[R]>t) --R;
Q[++R]=t;
}
if(L<=R) A[i][j]=Q[L];
}
}
rep(j,1,m) {
int ry=j+d,now=0;
L=1,R=0;
rep(i,1,n) {
int lx=max(1,i-d),rx=min(n,i+d);
while(L<=R && Q[L].x<lx) ++L;
while(now<rx) {
++now;
if(!A[now][ry].x) continue
while(L<=R && Q[R]>A[now][ry]) R--;
Q[++R]=A[now][ry];
}
if(s[i][j]!='X' && L<=R && Q[L].x && Q[L].d+1<dis[i][j]) dis[i][j]=Q[L].d+1,pre2[i][j]=make_pair(Q[L].x,Q[L].y);
}
}
//Part3
rep(i,1,n) rep(j,1,m) B[++cnt]=(Node){i,j,dis[i][j]};
sort(B+1,B+cnt+1);
rep(i,1,cnt) que.push(make_pair(B[i].x,B[i].y));
while(!que.empty()) {
int x=que.front().first,y=que.front().second; que.pop();
rep(i,0,3) {
int x1=x+z[i][0],y1=y+z[i][1];
if(dis[x1][y1]>dis[x][y]+1 && s[x1][y1]!='X') dis[x1][y1]=dis[x][y]+1,pre3[x1][y1]=make_pair(x,y),que.push(make_pair(x1,y1));
}
}
//输出方案
if(dis[fx][fy]==INF) return puts("-1"),0;
int x=fx,y=fy;
ansx[++ansc]=x,ansy[ansc]=y;
while(pre3[x][y].first) {
auto t=pre3[x][y];
x=t.first,y=t.second;
ansx[++ansc]=x,ansy[ansc]=y;
}
if(pre2[x][y].first) {
auto t=pre2[x][y];
x=t.first,y=t.second;
ansx[++ansc]=x,ansy[ansc]=y;
}
while(pre1[x][y].first) {
auto t=pre1[x][y];
x=t.first,y=t.second;
ansx[++ansc]=x,ansy[ansc]=y;
}
printf("%d\n",ansc-1);
drep(i,ansc,1) printf("%d %d\n",ansx[i]-1,ansy[i]-1);
}