题目描述太长了,我就不写了。
这是题目链接NOIP2013华容道
思路:
首先我不得不说一句,这是一道图论题,是的你没听错,图论题!!!但是这很明显是一道BFS好不!!!总之,这题需要用到SPFA。
但是你只要有超强的脑洞(对于我是不存在的),这道题就能很愉(bian)悦(tai)地解决了,可是这依然改变不了这是NOIP2013最恶心的题的事实。
首先我们发现一些细节问题:这题有多次询问,但是棋盘的格局是不变的;空白格子只有一个,目标棋子和目标位置也只有一个。那么我们就开始脑洞了。首先,我们发现目标棋子能移动当且仅当空白格子位于目标棋子的四周,这样我们就可以通过步数+1来移动目标棋子(实际上可以认为是目标棋子与空白格子交换位置),这样以后我们对整张棋盘进行预处理。令f[i][j][k][h]为目标棋子在坐标(i,j),空白格子在目标棋子的k方向上,转移到h方向上所需的最小步数,因为转移的过程中可能有障碍物,步数不能直接通过计算得出,所以我们要对每个点进行BFS。令d[i][j][k]表示目标棋子在(i,j),空白格子在k方向上的一个状态,并开一个结构体数组l,可以认为l是d的一个反向映射(我自己这么认为的,就是知道了一个确定的状态d[i][j][k],我们可以通过l数组的相应值得出当前d数组的i,j,k)。因为棋盘不变,所以预处理可以减少相当多的计算量,降低时间复杂度。
综上,我们需要两遍BFS,第一遍BFS对于每个点BFS,预处理出f[i][j][k][h]的值;第二遍BFS,把空白格子移动到目标棋子的四个方向上,各自记录一下状态及步数,并设置为最短路的初始状态。接下来我们就可以最短路乱搞了,把状态d当做点,把当前可到达的状态当做能通过边到达的点,把f数组当做边权,跑SPFA,就行了。
还有一个细节问题:当前点向四个方向移动时,可以证明,空白格子先转移到要移动方向的反方向再与目标棋子交换,这样一次移动步数和最小,所以我们可以处理边权问题。
PS:代码长到怀疑人生了。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
const int N=30+5;
const int M=10000+5;
int read()
{
int ret=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')
{ret=ret*10+ch-'0';ch=getchar();}
return ret*f;
}
int n,m,mmp[N][N];
const int dx[]={0,1,0,-1,0};
const int dy[]={0,0,1,0,-1};
int f[N][N][5][5];
int d[N][N][5];
bool vis[N][N];
struct node{
int x,y,k;
}l[M];
int dist[M],tot=0;
bool inq[M];
int fan[]={0,3,4,1,2};
struct zt{
int x,y,step;
};
bool can(int x,int y)
{
if(x>0&&x<=n&&y>0&&y<=m&&mmp[x][y]) return 1;
return 0;
}
queue<zt> q1;
queue<int> q2;
void init()
{
while(!q1.empty()) q1.pop();
memset(vis,0,sizeof(vis));
}
int bfs1(int x,int y,int k,int h)
{
init();
vis[x][y]=1;
int sx=x+dx[k],sy=y+dy[k];
int tx=x+dx[h],ty=y+dy[h];
q1.push((zt){sx,sy,0});
while(!q1.empty())
{
zt F=q1.front();
q1.pop();
if(F.x==tx&&F.y==ty) return F.step;
for(int i=1;i<=4;i++)
{
zt T;
T.x=F.x+dx[i],T.y=F.y+dy[i];
if(can(T.x,T.y)&&!vis[T.x][T.y])
{
T.step=F.step+1;
q1.push(T);
vis[T.x][T.y]=1;
}
}
}
return -1;
}
void bfs2(int x,int y,int tx,int ty)
{
init();
vis[x][y]=1,vis[tx][ty]=1;
q1.push((zt){x,y,0});
while(!q1.empty())
{
zt F=q1.front();
q1.pop();
for(int i=1;i<=4;i++)
{
if(tx+dx[i]==F.x&&ty+dy[i]==F.y)
{
int dd=d[tx][ty][i];
q2.push(dd);
inq[dd]=1;
dist[dd]=F.step;
break;
}
}
for(int i=1;i<=4;i++)
{
zt T;
T.x=F.x+dx[i],T.y=F.y+dy[i];
if(can(T.x,T.y)&&!vis[T.x][T.y])
{
T.step=F.step+1;
q1.push(T);
vis[T.x][T.y]=1;
}
}
}
}
void make_f()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(mmp[i][j])
{
for(int k=1;k<=4;k++)
{
if(can(i+dx[k],j+dy[k]))
{
for(int h=1;h<=4;h++)
{
if(can(i+dx[h],j+dy[h]))
{
int w=bfs1(i,j,k,h);
if(w!=-1) f[i][j][k][h]=w;
}
}
}
}
}
}
}
void make_l()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(mmp[i][j])
{
for(int k=1;k<=4;k++)
{
d[i][j][k]=++tot;
l[tot].x=i;
l[tot].y=j;
l[tot].k=k;
}
}
}
}
int spfa(int tx,int ty)
{
while(!q2.empty())
{
int u=q2.front();
q2.pop();
inq[u]=0;
int x=l[u].x,y=l[u].y,k=l[u].k;
for(int i=1;i<=4;i++)
{
int zx=x+dx[i],zy=y+dy[i];
if(can(zx,zy))
{
int v=d[zx][zy][fan[i]];
if(dist[v]>dist[u]+f[x][y][k][i]+1)
{
dist[v]=dist[u]+f[x][y][k][i]+1;
if(!inq[v])
{
q2.push(v);
inq[v]=1;
}
}
}
}
}
int ans=0x3fffffff;
for(int i=1;i<=4;i++) ans=min(ans,dist[d[tx][ty][i]]);
if(ans>1e9) ans=-1;
return ans;
}
int main()
{
int Z;
scanf("%d%d%d",&n,&m,&Z);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mmp[i][j]=read();
make_f();
make_l();
while(Z--)
{
int EX,EY,SX,SY,TX,TY;
EX=read(),EY=read(),SX=read(),SY=read(),TX=read(),TY=read();
memset(dist,0x3f,sizeof(dist));
bfs2(EX,EY,SX,SY);
if(SX==TX&&SY==TY) printf("0\n");
else printf("%d\n",spfa(TX,TY));
}
return 0;
}