Legend
Link \(\textrm{to Luogu}\)。
题目描述就不搬了。
Editorial
显然,可以看做空位在一直移动,移动到目标棋子旁边就可以交换一下空位和目标棋子的位置,最后要让目标棋子到目标位置。
有一个非常显然的做法记录节点状态为:【空位坐标】+【目标棋子坐标】的形式。直接 0-1 bfs 就是 \(O(Tn^2m^2)\) 的,足够拿到 \(60\) 分。
想办法优化状态,有什么状态是无效的?显然,目标棋子会移动当且仅当空位在目标棋子相邻格子。
不难想到把节点状态改为:【目标棋子坐标】+【空位在目标棋子上下左右哪个方向】。这样状态数量就变成了 \(4nm\)。
那么如何转移状态?我们可以预处理一个 \(dist\) 数组 \(dist[a][b][c][d][e][f]\)
表示空位初始在 \((a,b)\),目标棋子在 \((c,d)\),空位要走到 \((e,f)\) 的最短路长度。
(在这里目标棋子没有什么用,仅仅看做一个障碍,空位不能移动到上面去)
这就有问题了:这样状态数不就又变成 \(O(n^3m^3)\) 了吗?
其实不然,我们发现由于上面已经进行了无效状态的去除,所以目标棋子一定会在初始空位的上下左右,这样状态数就又变成了 \(4n^2m^2\)。
预处理只要 bfs,复杂度与状态数相同,就是 \(O(n^2m^2)\)。
还有唯一一个问题:最初的时候空位并不一定在目标棋子四周。所以我们要强行把它移动过去——但我们怎么知道移动过去的代价呢?
其实我们调用 \(dist\) 数组就可以了,由于转移是双向的,可以看做从目标棋子四周移动到初始位置。
接着我们用 Dijkstra 跑出答案就可以啦!复杂度是 \(O(n^2m^2+Tnm \log (nm))\)
Code
使用这种方法要特判起点与终点相同的情况。
#include
#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
freopen(#x".in" ,"r" ,stdin);\</