玄学的搜索与建图...
首先经过分析,可以得出几个结论:
①.移动棋子等价于移动空格
原因:如果一个棋子可以移动,那么这个棋子一定是和旁边的空格交换了位置,所以也就相当于是移动空格
②.有价值的图的状态是有限的
原因:我们的目标是把起始棋子移到结束位置上,那么如果能够移动起始棋子,一定是把空格移到了起始位置旁边,那么根据结论①,我们只需把这个状态转移到空格子在目标位置附近就完成任务了!
那么根据上述结论,我们可以构造一个状态num[i][j][k],表示空格在点(i,j)的哪一方向(第三维用四个数字代表上下左右)
这个状态数是很少的。
接下来,我们只需考虑转移即可。
我们发现,比如我们想从某一状态num[i][j][k]转移到另一状态num[i][j]k`](注意i,j不变),那么首先,点i,j不能动,这样进行一个bfs,就可以求出代价,然后把这两个状态连边
当然,也存在一种情况就是直接交换空格和i,j,但这样就是由状态num[i][j][k]变成了状态num[i'][j'][k'],其中i' j'是这个点周围的三个点(显然要除掉空格),而k'是这个点i,j相对于原三个点的位置(上下左右),这样的代价是1,同样连边即可
最后对所有状态跑一遍最短路spfa,要求初始状态是空格在起点四周,通过bfs求出初值,终止状态是空格在终点四周,通过最短路由初状态更新即可
注意如果最短路始终为正无穷即为无解
上代码(自认为全站最好看,0压行)
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct Edge
{
int next;
int to;
int val;
}edge[300005];
int head[3005];
int cnt=1;
struct node
{
int x,y,d;
};
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void add(int l,int r,int w)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
edge[cnt].val=w;
head[l]=cnt++;
}
int n,m,q;
int maps[35][35];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int num[35][35][4];
bool flag[35][35];
int dis[50005];
int sx,sy,ex,ey,bx,by;
int bfs(int stx,int sty,int edx,int edy,int rtx,int rty)
{
if(stx==edx&&sty==edy)
{
return 0;
}
memset(flag,0,sizeof(flag));
queue <node> M;
node u0;
u0.x=stx;
u0.y=sty;
u0.d=0;
M.push(u0);
while(!M.empty())
{
node u=M.front();
M.pop();
int x=u.x;
int y=u.y;
int d=u.d;
for(int i=0;i<=3;i++)
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(!maps[xx][yy])
{
continue;
}
if(xx==rtx&&yy==rty)
{
continue;
}
if(flag[xx][yy])
{
continue;
}
if(xx==edx&&yy==edy)
{
return d+1;
}
flag[xx][yy]=1;
node tt;
tt.x=xx;
tt.y=yy;
tt.d=d+1;
M.push(tt);
}
}
return 0x3f3f3f3f;
}
int spfa()
{
if(sx==ex&&sy==ey)
{
return 0;
}
memset(dis,0x3f,sizeof(dis));
queue <int> M;
for(int i=0;i<=3;i++)
{
int x=sx+dir[i][0];
int y=sy+dir[i][1];
if(num[sx][sy][i])
{
dis[num[sx][sy][i]]=bfs(bx,by,x,y,sx,sy);
M.push(num[sx][sy][i]);
}
}
while(!M.empty())
{
int u=M.front();
M.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(dis[to]>dis[u]+edge[i].val)
{
dis[to]=dis[u]+edge[i].val;
M.push(to);
}
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<=3;i++)
{
if(num[ex][ey][i])
{
ans=min(ans,dis[num[ex][ey][i]]);
}
}
if(ans==0x3f3f3f3f)
{
return -1;
}else
{
return ans;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
init();
int tot=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&maps[i][j]);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=0;k<=3;k++)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if(maps[i][j]&&maps[x][y])
{
num[i][j][k]=++tot;
}
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=0;k<=3;k++)
{
if(num[i][j][k])
{
int x=i+dir[k][0];
int y=j+dir[k][1];
add(num[i][j][k],num[x][y][k^1],1);//交换空点和旁边的点
}
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=0;k<=3;k++)
{
for(int t=0;t<=3;t++)
{
if(k!=t&&num[i][j][k]&&num[i][j][t])
{
int x1=i+dir[k][0];
int y1=j+dir[k][1];
int x2=i+dir[t][0];
int y2=j+dir[t][1];
add(num[i][j][k],num[i][j][t],bfs(x1,y1,x2,y2,i,j));
}
}
}
}
}
while(q--)
{
scanf("%d%d%d%d%d%d",&bx,&by,&sx,&sy,&ex,&ey);
printf("%d
",spfa());
}
return 0;
}