题目描述
【问题描述】
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的
在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
有些棋子是固定的,有些棋子则是可以移动的;
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。
游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的, 但是棋盘上空白的格子的初始位置、 指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次
玩的时候, 空白的格子在第 EXi 行第 EYi 列,指定的可移动棋子的初始位置为第 SXi 行第 SYi列,目标位置为第 TXi 行第 TYi 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
输入输出格式
输入格式:
第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;
接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。接下来的 q 行,每行包含 6 个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。
输出格式:
输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。
注意
这道题记录状态不光只记录没一个点的坐标 [x][y],还要记录记录 来向,也就是通过哪种移动走到点[x][y]的,其实记成前趋坐标也没问题,属于压缩状态了。
有时候不记录这个点的具体坐标,反而记录的是前趋坐标,以及到达方式。要留心看好。
每一次搜的if条件不一样
整体思路
1、 在把图输入之后,就预处理每一个点到各个点的步数。
— 是bfs没毛病,准确来说是 每一个点通过何种方式到各个点的步数
— 而且全程不允许经过初始点(每一次bfs的起点),也就是说只能搜到不需跨过初始点就可以到达的点
于是就可以得到如下算法:
初始点不入队,而是四个相邻点入队(并记录来向dir)其实很好记的啦,如果入门的时候做过八皇后之类题就是什么操作的
再开一个次级队列,进行一本正经的dfs(不记录来向),得到一组dis,搜完了存进 含来向和去向的最终路权路权是我们算出来的
其实这一次大搜索只为处理出初始点的四个相邻点的三个相邻点(第四个方向是初始点,没必要)
觉得很费力气?直接赋值就好了?我也是这样想的,原作者说是为了防止直接到不了,我觉得好似不存在,总之保险hh
2、 正式输入某一特定情况后,我们急于利用预处理的路权跑spfa,可惜的是,我们的空格和出发点s不是一个点的。
于是我们需要把空格 拉到 s 附近,既然是附近,那就是保持s的初始状态,所以搜索时不可以动s点。
(说实话,这道题每一次搜索的处理前条件不完全相同,要想明白目的)
于是就可以得到如下算法:
不带方向地进行预搜索,bfs即可,第一次碰到,即最短
3、当空格在s附近我们就可以开始移动了
于是就不得不得到如下算法:
带方向的给s点四个方向的点赋路权(预搜索得到),并带方向地把s入队 (和预处理不同,不存四周点,存原点与原点去向)
进行SPFA从现在开始s已经完成存在的使命,空格代替s开始自由飞翔,回顾一下就是,利用s与e之间的距离给s周围的四个点赋最初路权,即“把空格 拉到 s 附近”,然后利用预处理路权进行最短路算法
每一个队首的点u,需要首先利用”直接交换的性质松弛 u 指向 的那个点 (这里有个漂亮的换向操作)
这个时候我就可以解释这个向的作用了,就是对于任意点x,他的指向就是空格在的地方,来表示这个点的可移动趋势。
然后再进行其余三向点的松弛,这个时候可以跨过s了,但是这个时候我们松弛的不是s的四周点,是在另外三周方向下的s点
这个时候就可以解释我刚才蒙混过关没说的一个点:为什么赋路权要全图bfs,因为对于任意的点x,让空格从方向1到方向2可能需要跨过障碍物,就比如右上角,左下角,左上角,右下角的位置可能不可以移动。
然后判一下四个来向下的终点路径 其实叫它来向挺对的,因为,任意一个点x,他的空格在哪个方向,他就是从哪个方向来的,如果不是初始值的话。
代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
int dx[5]={0 ,-1,0,1};
int dy[5]={-1, 0,1,0};
struct dogs{int x,y,dir;}dog[1000];
struct cats{int x,y;}cat[1000];
int map[50][50],dis[50][50],cos[50][50][5][5],dp[50][50][5];
bool vis[50][50][5];
int Sx,Sy,Ex,Ey,Tx,Ty,n,m,q;
void prebfs(int sx,int sy)
{
queue<dogs> st;
for(int i=0;i<4;i++)
{
int xx=sx+dx[i],yy=sy+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&map[xx][yy])
{
st.push((dogs){xx,yy,i});
//cout<<xx<<" "<<yy<<" "<<i<<endl;
}
}
//cout<<"~~~~~~~~~~~~~"<<endl;
while(!st.empty())
{
dogs b=st.front();st.pop();
mem(dis,0x3f);
int bx=b.x,by=b.y,ret=b.dir;
queue<cats>q;
q.push((cats){bx,by});
dis[bx][by]=0;
//cout<<"("<<bx<<","<<by<<")="<<ret<<endl;
while(!q.empty())
{
cats u=q.front();q.pop();
for(int i=0;i<4;i++)
{
int xx=u.x+dx[i],yy=u.y+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&map[xx][yy]&&(xx!=sx||yy!=sy)&&dis[xx][yy]==0x3f3f3f3f)
{
dis[xx][yy]=dis[u.x][u.y]+1;
q.push((cats){xx,yy});
//cout<<xx<<" "<<yy<<" "<<dis[xx][yy]<<endl;
}
}
}
//cout<<"____________"<<endl;
for(int i=0;i<4;i++)
{
int xx=sx+dx[i],yy=sy+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&map[xx][yy]&&(xx!=bx||yy!=by)&&dis[xx][yy]!=0x3f3f3f3f)
{
cos[sx][sy][ret][i]=dis[xx][yy];
//cout<<cos[sx][sy][ret][i]<<" ";
}
//cout<<endl;
}
}
}
void preblock()
{
queue<cats>q;
mem(dis,0x3f);
dis[Ex][Ey]=0;
q.push((cats){Ex,Ey});
while(!q.empty())
{
cats u=q.front();q.pop();
for(int i=0;i<4;i++)
{
int xx=u.x+dx[i],yy=u.y+dy[i];
if(xx>0&&yy>0&&xx<=n&&yy<=m&&map[xx][yy]&&
(xx!=Sx||yy!=Sy)&&dis[xx][yy]==0x3f3f3f3f)
{
dis[xx][yy]=dis[u.x][u.y]+1;
q.push((cats){xx,yy});
}
}
}
}
int bfs()
{
if(Sx==Tx&&Sy==Ty)return 0;
preblock();
queue<dogs>q;
mem(dp,0x3f);mem(vis,0);
for(int i=0;i<4;i++)
{
int xx=Sx+dx[i];
int yy=Sy+dy[i];
if(xx>0&&yy>0&&xx<=n&&yy<=m&&map[xx][yy]&&dis[xx][yy]!=0x3f3f3f3f)
{
dp[Sx][Sy][i]=dis[xx][yy];//这里为什么不是xx yy?
q.push((dogs){Sx,Sy,i});
vis[Sx][Sy][i]=1;
}
}
while(!q.empty())
{
dogs u=q.front();q.pop();
int ux=u.x,uy=u.y,ret=u.dir;vis[ux][uy][ret]=0;//spfa不可以忘了消去标记
int xx=ux+dx[ret],yy=uy+dy[ret];//不找对点,直接用uxuy交换。
if(dp[xx][yy][(ret+2)%4]>dp[ux][uy][ret]+1)
{
dp[xx][yy][(ret+2)%4]=dp[ux][uy][ret]+1;
if(!vis[xx][yy][(ret+2)%4])
{
q.push((dogs){xx,yy,(ret+2)%4});
vis[xx][yy][(ret+2)%4]=1;
}
}
for(int i=0;i<4;i++)
{
int xx=ux+dx[i],yy=uy+dy[i];
if(xx>0&&yy>0&&xx<=n&&yy<=m&&map[xx][yy]&&
i!=u.dir&&cos[ux][uy][u.dir][i]!=0x3f3f3f3f)
//这里为什么可以经过SxSy了呢?为什么i!=dir?
/* if(dp[xx][yy][i]>dp[ux][uy][u.dir]+cos[ux][uy][u.dir][i])
dp[xx][yy][i]=dp[ux][uy][u.dir]+cos[ux][uy][u.dir][i];
if(!vis[xx][yy][i])
q.push((dogs){xx,yy,i});
vis[xx][yy][i]=1;
*/
if(dp[ux][uy][i]>dp[ux][uy][u.dir]+cos[ux][uy][u.dir][i])
{
dp[ux][uy][i]=dp[ux][uy][u.dir]+cos[ux][uy][u.dir][i];
if(!vis[ux][uy][i])
{
q.push((dogs){ux,uy,i});
vis[ux][uy][i]=1;
}
}
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<4;i++)
{
ans=min(ans,dp[Tx][Ty][i]);
}
if(ans==0x3f3f3f3f)return -1;
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&map[i][j]);
mem(cos,0x3f);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(map[i][j])prebfs(i,j);
for(int i=1;i<=q;i++)
{
scanf("%d%d%d%d%d%d",&Ex,&Ey,&Sx,&Sy,&Tx,&Ty);
printf("%d\n",bfs());
}
//system("pause");
return 0;
}