题目描述
解题思路(IDDFS)
一看这个题,想不出什么神奇的方法,再想一下,直接搜索吧
搜索的顺序很显然,从空地开始,往八个方向进行搜索,如果某一次的操作能得到目标矩阵,就可以退出了
因为题目限制了最多
15
15
15步,就很明显是IDDFS
了,但如果只是限制一个上限深度还不够,仍然会超时
此时我们就需要用到乐观估计函数了,每一道题的乐观估计函数是不同的,针对这道题,如果在当前状态下要还原到目标矩阵,最理想的状态就是每用一步就可以让这个棋子到位,所以我们只需要统计当前矩阵和目标矩阵不同的元素个数来作为乐观估计函数就可以了
参考代码(IDDFS)
#include <cstdio>
#include <cstring>
template <class T>
T read() {
T x = 0; T f = 1; char s = getchar();
while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - 48; s = getchar();}
return x * f;
}
template <typename T>
void wri(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x / 10) wri(x / 10);
putchar(x % 10 + 48);
}
template <typename T>
void write(T x, char s) {
wri(x);
putchar(s);
}
int n, sx, sy;
int dir[8][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};
char a[10][10], s[10][10] = {{'0', '0', '0', '0', '0', '0'}, {'0', '1', '1', '1', '1', '1'}, {'0', '0', '1', '1', '1', '1'}, {'0', '0', '0', '*', '1', '1'}, {'0', '0', '0', '0', '0', '1'}, {'0', '0', '0', '0', '0', '0'}}; //s为目标矩阵
bool f;
int check() { //乐观估计函数
int ret = 0;
for(int i = 1;i <= 5;i ++)
for(int j = 1;j <= 5;j ++)
if(a[i][j] != s[i][j])
ret ++;
return ret;
}
bool inside(int x, int y) {
if(x < 1 || x > 5 || y < 1 || y > 5) return 0;
return 1;
}
void IDDFS(int nowDepth, int maxDepth, int x, int y) {
if(nowDepth > maxDepth) return ;
if(check() == 0) {f = 1; return ;} //如果已经得到目标矩阵
for(int i = 0;i <= 7;i ++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(! inside(tx, ty)) continue;
a[x][y] = a[tx][ty], a[tx][ty] = '*';
if(nowDepth + check() <= maxDepth) //如果当前的深度加上最乐观的情况都会超过限制深度则退出,否则进行下一层搜索
IDDFS(nowDepth + 1, maxDepth, tx, ty);
a[tx][ty] = a[x][y], a[x][y] = '*';
if(f) return ;
}
}
int main() {
scanf("%d", &n);
while(n --) {
f = 0;
for(int i = 1;i <= 5;i ++) {
scanf("\n");
for(int j = 1;j <= 5;j ++)
scanf("%c", &a[i][j]);
}
for(int i = 1;i <= 5;i ++)
for(int j = 1;j <= 5;j ++)
if(a[i][j] == '*')
sx = i, sy = j;
for(int i = 0;i <= 15;i ++) {
IDDFS(0, i, sx, sy);
if(f) {
write(i, '\n');
break;
}
}
if(! f)
puts("-1");
}
return 0;
}
解题思路(双向BFS)
正向起点为起始棋盘,反向起点为目标棋盘,这种方法的难点就是判重,如何确定当前状态是否被访问过
由于题目除空格外只有两种棋子,我们可以将其看作是一个二进制数,对每一个位置进行编号(除去空格的位置),然后算出其十进制的值
但这样是会出现重复情况的,例如这样两个棋盘(*号用2代替):
[
12111
11111
11100
00000
00000
]
\begin{bmatrix} 12111 \\ 11111\\ 11100\\ 00000\\ 00000\end{bmatrix}
⎣⎢⎢⎢⎢⎡1211111111111000000000000⎦⎥⎥⎥⎥⎤和
[
11211
11111
11100
00000
00000
]
\begin{bmatrix}11211\\ 11111\\ 11100\\ 00000\\ 00000\end{bmatrix}
⎣⎢⎢⎢⎢⎡1121111111111000000000000⎦⎥⎥⎥⎥⎤因为我们编号是不算
2
2
2的,所以这样算出来的两个数就会相同,所以我们需要另外开一维数组来记录二的编号
参考代码(双向BFS)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
template <class T>
T read() {
T x = 0; T f = 1; char s = getchar();
while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - 48; s = getchar();}
return x * f;
}
template <typename T>
void wri(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x / 10) wri(x / 10);
putchar(x % 10 + 48);
}
template <typename T>
void write(T x, char s) {
wri(x);
putchar(s);
}
template <typename T>
T Max(T x, T y) {return x > y ? x : y;}
template <typename T>
T Min(T x, T y) {return x < y ? x : y;}
template <typename T>
T Fabs(T x) {return x < 0 ? -x : x;}
template <typename T>
void Swap(T &x, T &y) {T t = x; x = y, y = t;}
struct node {
int f, idxx, idxy;
int p[10][10];
};
node start, goal;
queue <node> que;
map <int, bool> vis[2][30];
map <int, int> step[2][30];
int n;
int dir[8][2] = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
char s;
int qkpow(int x, int y) {
int ret = 1;
while(y) {
if(y & 1)
ret = ret * x;
x = x * x;
y >>= 1;
}
return ret;
}
int getIdx(node x) { //求出空格的位置
for(int i = 1;i <= 5;i ++)
for(int j = 1;j <= 5;j ++)
if(x.p[i][j] == 2)
return (i - 1) * 5 + j;
return 0;
}
int getHash(node x) { //判重hash
int ret = 0, sum = 0;
for(int i = 1;i <= 5;i ++)
for(int j = 1;j <= 5;j ++) {
if(x.p[i][j] != 2) sum ++;
if(x.p[i][j] == 1) ret += qkpow(2, sum);
}
return ret;
}
bool inside(int x, int y) {
if(x < 1 || x > 5 || y < 1 || y > 5) return 0;
return 1;
}
int DWBFS() { //双向BFS
que.push(start);
que.push(goal);
int idx1 = getIdx(start), idx2 = getIdx(goal);
int sum1 = getHash(start), sum2 = getHash(goal);
vis[start.f][idx1][sum1] = vis[goal.f][idx2][sum2] = 1;
while(! que.empty()) {
node t = que.front();
que.pop();
idx1 = getIdx(t);
sum1 = getHash(t);
if(vis[! t.f][idx1][sum1])
return step[t.f][idx1][sum1] + step[! t.f][idx1][sum1] <= 15 ? step[t.f][idx1][sum1] + step[! t.f][idx1][sum1] : -1;
for(int i = 0;i <= 7;i ++) {
node tmp = t;
tmp.idxx += dir[i][0];
tmp.idxy += dir[i][1];
if(! inside(tmp.idxx, tmp.idxy)) continue;
Swap(tmp.p[tmp.idxx][tmp.idxy], tmp.p[t.idxx][t.idxy]);
idx2 = getIdx(tmp);
sum2 = getHash(tmp);
if(! vis[tmp.f][idx2][sum2]) {
vis[tmp.f][idx2][sum2] = 1;
step[tmp.f][idx2][sum2] = step[t.f][idx1][sum1] + 1;
if(step[tmp.f][idx2][sum2] < 8)
que.push(tmp);
}
}
}
return -1;
}
int main() {
goal.p[1][1] = goal.p[1][2] = goal.p[1][3] = goal.p[1][4] = goal.p[1][5] = goal.p[2][2] = goal.p[2][3] = goal.p[2][4] = goal.p[2][5] = goal.p[3][4] = goal.p[3][5] = goal.p[4][5] = 1, goal.p[3][3] = 2;
goal.f = 0, goal.idxx = 3, goal.idxy = 3;
scanf("%d", &n);
while(n --) {
while(! que.empty()) que.pop();
for(int i = 0;i <= 1;i ++)
for(int j = 1;j <= 25;j ++)
vis[i][j].clear(), step[i][j].clear();
for(int i = 1;i <= 5;i ++) {
s = getchar();
for(int j = 1;j <= 5;j ++) {
s = getchar();
if(s == '*') {start.p[i][j] = 2, start.idxx = i, start.idxy = j;}
else start.p[i][j] = s - 48;
}
}
start.f = 1;
write(DWBFS(), '\n');
}
return 0;
}