这题和八数码问题是一样的,但是我还是不会写。。。
参考了shuangde800的解题报告。
说是参考,其实就是看一句理解一句写一句。。。对哈希表和八数码有了一个初步的了解。
还有这篇文章各种常用字符串Hash函数比较,很厉害的样子。。
因为以前都是用结构体+指针方式写链表,突然看到数组表示的,很不习惯。
在这里理解了很久。
inline int TryToInsert(int s)
{
int h = BKDRHash(que[s]);
int u = head[h];
while (u)
{
if (memcmp(que[u], que[s], sizeof(que[s])) == 0)
return 0;
u = next[u];
}
next[s] = head[h]; //把当前的哈希值的首部放到后面
head[h] = s; //放到对应的哈希值的首部
return 1;
}
如果没有冲突的哈希值,一切好说。
如果有冲突的,就顺着链表找下去,如果找不到,说明这个状态没有到达过,把它插入到哈希值的第一位,连接原本的第一位和现在的第一位。
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 5000000;
const int HashSize = 3000000;
typedef char State[5][5];
State que[MAXN], start, goal = {{'1','1','1','1','1'},
{'0','1','1','1','1'},
{'0','0',' ','1','1'},
{'0','0','0','0','1'},
{'0','0','0','0','0'}};
int dir[][2] = {{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}, {1, 2}, {2, 1}, {2, -1}, {1, -2}};
int step[MAXN], ans;
int head[HashSize], next[MAXN];
inline int BKDRHash(State &s);
inline int TryToInsert(int s);
void BFS();
int main()
{
//freopen("input.txt", "r", stdin);
int i, j, T;
scanf("%d%*c", &T);
while (T--)
{
for (i = 0; i < 5; i++)
gets(start[i]);
BFS();
if (ans != -1)
printf("Solvable in %d move(s).\n", ans);
else
printf("Unsolvable in less than 11 move(s).\n");
}
return 0;
}
inline int BKDRHash(State &s)
{
unsigned int seed = 131;
unsigned int hashValue = 0;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
hashValue = hashValue * seed + s[i][j];
return (hashValue & 0x7FFFFFF) % HashSize;
}
inline int TryToInsert(int s)
{
int h = BKDRHash(que[s]);
int u = head[h];
while (u)
{
if (memcmp(que[u], que[s], sizeof(que[s])) == 0)
return 0;
u = next[u];
}
next[s] = head[h]; //把当前的哈希值的首部放到后面
head[h] = s; //放到对应的哈希值的首部
return 1;
}
void BFS()
{
memset(head, 0, sizeof(head)); //Initial the hash table
int front = 0, rear = 1;
memcpy(que[0], start, sizeof(start));
step[0] = 0;
while (front < rear)
{
State &s = que[front];
if (step[front] > 10)
{
ans = -1;
return;
}
if (memcmp(goal, s, sizeof(s)) == 0)
{
ans = step[front];
return;
}
int x, y;
bool flag = false;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
{
if (flag)
break;
if (s[i][j] == ' ')
{
x = i, y = j;
flag = true;
break;
}
}
for (int i = 0; i < 8; i++)
{
int dx = x + dir[i][0];
int dy = y + dir[i][1];
if (dx >= 0 && dx < 5 && dy >= 0 && dy < 5)
{
State &t = que[rear];
memcpy(t, s, sizeof(s));
t[dx][dy] = s[x][y];
t[x][y] = s[dx][dy]; //移动空格
step[rear] = step[front] + 1;
if (TryToInsert(rear)) //如果不存在重复的,就放到队列里。
rear++;
}
}
front++;
}
ans = -1;
}