题目链接
首先这是一道毒瘤题,写了一下午没有瞎弄出来,看了题解才有所领悟
以上都是一些废话
分析
开始想过70分的暴力,直接暴力BFS,答案离线,起点相同的一起做;试过不止70分
首先很容易看出来这是一道图论题,显然是要用到最短路,确定d数组状态d[i][j][k]表示起点到(i,j),k方向是白块的最短路径,但是由于图的特性,建图,好像有点鬼畜,死磕2小时无所获,于是果断看题解
有一个特点就是事实上移动棋子就是移动白块;
格子S想要走到与它相邻的k方向(left,right,up,down)上的格子,必须先把E格子通过w的花费移到某个格子,然后在用1的花费使S走到那 里,总花费为w+1,由于从一个格子向相邻方向上走一步的花费要多次使用,因此我们把它预处理成数组a[x][y][k][h],表示从(x,y)这个棋子,白格子在k方向上,使棋子走到h方向上相邻的格子的最少花费,这个可以用BFS预处理
然后就是最短路的过程,模板化,数据用spfa就能过,好吧我懒得写Dijkstra
答案在先处理就好了。代码附注释。
#include<bits/stdc++.h>
#define up 1
#define down 2
#define left 3
#define right 4//方向
#define N 35
#define inf 0x3f3f3f3f
using namespace std;
struct node{int x,y,k;};//点状态
int p[N][N],d[N][N][5],Ex,Ey,a[N][N][5][5],deep[N][N],n,m;
bool in[N][N][5],done[N][N];
queue<node>q,qb;
int other(int k){
if(k==up)return down;
if(k==down)return up;
if(k==left)return right;
if(k==right)return left;
} //相反方向移动
node go(node n,int k){
node t=n;
if(k==up)t.x--;
if(k==down)t.x++;
if(k==left)t.y--;
if(k==right)t.y++;
return t;
} //移动棋子
int bfs(node s,node t){
if(p[s.x][s.y]==0||p[s.x][s.y]==0)return inf;//如果两点中有固定点,则不能到达
memset(deep,0x3f,sizeof(deep));
memset(done,0,sizeof(done));
qb=*(new queue<node>);//清空队列很重要<s>被坑</s>
qb.push(s);
deep[s.x][s.y]=0;done[s.x][s.y]=true;
while(!qb.empty()&&!done[t.x][t.y]){
node u=qb.front();
qb.pop();
for(int k=1;k<=4;k++){
node v=go(u,k);
if(!done[v.x][v.y]&&p[v.x][v.y]==1){
done[v.x][v.y]=true;
qb.push(v);
deep[v.x][v.y]=deep[u.x][u.y]+1;
}
}
}
return deep[t.x][t.y];
}//基本宽搜操作
void init(){
memset(a,0x3f,sizeof(a));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(p[i][j]==0)continue;
p[i][j]=0;//把点封死,防止冗余的BFS
for(int k=1;k<=4;k++){
for(int h=1;h<=4;h++){
if(h<k){
a[i][j][k][h]=a[i][j][h][k];
continue;
}//根据对称性,加快效率
node t1=go((node){i,j},k);
node t2=go((node){i,j},h);
if(p[t1.x][t1.y]==0||p[t2.x][t2.y]==0)continue;//判断固定点
a[i][j][k][h]=bfs(t1,t2)+1;//w+1的操作
}
}
p[i][j]=1;//恢复点的连通
}
}
int spfa(node S,node T){
if(S.x==T.x&&S.y==T.y)return 0;
memset(d,0x3f,sizeof(d));
memset(in,0,sizeof(in));
q=*(new queue<node>);
if(p[S.x][S.y]==0||p[T.x][T.y]==0)return inf;
p[S.x][S.y]=0;
for(int k=1;k<=4;k++){
q.push((node){S.x,S.y,k});
in[S.x][S.y][k]=true;
d[S.x][S.y][k]=bfs((node){Ex,Ey},go(S,k));//计算白格子到起点的步数
}//队列初始化
p[S.x][S.y]=1;
while(!q.empty()){
node u=q.front();
q.pop();
in[u.x][u.y][u.k]=false;
for(int h=1;h<=4;h++){
node v=go(u,h);
v.k=other(h);//移动后白格子所在的位置相反
if(d[u.x][u.y][u.k]+a[u.x][u.y][u.k][h]<d[v.x][v.y][v.k]){
d[v.x][v.y][v.k]=d[u.x][u.y][u.k]+a[u.x][u.y][u.k][h];
if(!in[v.x][v.y][v.k]){
q.push(v);
in[v.x][v.y][v.k]=true;
}
}//执行松弛操作
}
}
int res=inf;
for(int k=1;k<=4;k++)
res=min(res,d[T.x][T.y][k]);//得到答案
return res;
}
int main(){
int Q,sx,sy,tx,ty;
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&p[i][j]);
init();//预处理
for(int i=1;i<=Q;i++){
scanf("%d%d%d%d%d%d",&Ex,&Ey,&sx,&sy,&tx,&ty);
int ans=spfa((node){sx,sy},(node){tx,ty});
if(ans<inf)printf("%d\n",ans);
else printf("-1\n");
}//在线操作
return 0;
}