1. 使用全排列的编码/解码(最高效)
代码:
#include<cstring>
using namespace std;
typedef int State[9]; //State相当于 int [9]
const int MAXSTATE = 1000000;// 最多的状态数目
State st[MAXSTATE], goal; //st相当于intst[MAXSTATE][9];goal 相当于 int goal[9];
int dist[MAXSTATE];//存储距离
int vis[362880], fact[9];
// 9!=362880,vis中的下标是解码后的大小,fact中数组元素的值是编码的大小
void init_lookup_table() //编码函数:使得fact[n]=n!
{
fact[0] =1;
for(int i= 1; i < 9; i++)
{
fact[i] = fact[i-1] * i;
}
}
int try_to_insert(int s) //解码函数:将每一种状态对应在数组st 中的下标s映射到整数code
{
int code= 0;
for(int i= 0; i < 9; i++)
{
intcnt = 0;
for(int j = i+1; j < 9; j++)
{
if(st[s][j] < st[s][i])
{
cnt++;
}
}
code+= fact[8-i] * cnt;
}
if(vis[code]) //如果该状态已经被访问过,则不能再次访问,没有成功插入查找表
{
return 0;
}
returnvis[code] = 1; //标记已访问
}
const int dx[] = {-1, 1, 0, 0};// 四个方向
const int dy[] = {0, 0, -1, 1};
int bfs()
{
init_lookup_table(); //初始化查找表
int front= 1, rear = 2; //不使用下标0,因为0被视为“不存在”
while(front < rear)
{
State& s = st[front]; //使用引用简化代码
if(memcmp(goal,s, sizeof(s)) == 0)// 如果到达目标状态
{
return front;//返回目标状态的下标
}
intz;//标记数组中0的位置
for(z= 0; z < 9; z++)
{
if(!s[z]) break;
}
int x= z/3, y = z%3; //x:是0所在的行数;y是0所在的列数,两者的范围均为0—2
for(int d = 0; d < 4; d++) //依次向0周围的四个方向走去
{
int newx = x + dx[d];//0的新行数
int newy = y + dy[d];//0的新列数
int newz = newx * 3 + newy;//0的新下标
if(newx >= 0 && newx < 3 && newy >= 0&& newy < 3) //新位置没有越界
{
State& t = st[rear]; //下一个状态
memcpy(&t, &s, sizeof(s));//首先将上一个状态复制到新的状态中
t[newz] = s[z];//然后修改变化的值:0与其中一个值互换位置
t[z] = s[newz];
dist[rear] = dist[front] + 1;// 下一个状态距离初始状态的距离+1
if(try_to_insert(rear))
/*
成功插入查找表(该状态从未出现),修改队尾的指针。如果没有执行rear++语句,表明当前的状态st[rear]已经存在,现在又生成了一个相同的状态,所以状态出现重复,下次循环的时候去掉重复的状态,改为新状态下的后续状态。
*/
{
}
}
}
front++;
}
return 0;
}
int main()
{
for(int i= 0; i < 9; i++)
{
scanf("%d", &st[1][i]); //读入初始状态
}
for(int i= 0; i < 9; i++)
{
scanf("%d", &goal[i]); //读入目标状态
}
int ans =bfs();
if(ans> 0)
{
printf("%d\n", dist[ans]);
}
else
{
printf("-1\n");
}
return 0;
}
示例输入:
8 1 5 7 3 6 4 0 2
示例输出:
31
2. 使用哈希表(竞赛中最常用)
#include<cstring>
#include<set>
using namespace std;
typedef int State[9];
const int MAXSTATE = 1000000;
State st[MAXSTATE], goal;
int dist[MAXSTATE];
const int MAXHASHSIZE =1000003;
int head[MAXHASHSIZE],Next[MAXSTATE];
void init_lookup_table()
{
memset(head, 0, sizeof(head));
}
int hash(State& s)
{
int v = 0;
for(int i = 0; i < 9; i++)
{
v = v * 10 + s[i]; 将排列转换成九位的正整数
}
return v % MAXHASHSIZE; 确保hash函数值是不超过hash表的大小的非负整数
}
int try_to_insert(int s)
{
int h = hash(st[s]);
int u = head[h]; 从表头开始查找链表
while(u)
{
if(memcmp(st[u], st[s], sizeof(st[s]))== 0) 找到了插入失败
{
return 0;
}
u = Next[u];顺着链表,继续找
}
Next[s] = head[h]; 插入到链表中
head[h] = s;
return 1;
}
const int dx[] = {-1, 1, 0,0};
const int dy[] = {0, 0, -1,1};
int bfs()
{
init_lookup_table();
int front = 1, rear = 2;
while(front < rear)
{
State& s = st[front];
if(memcmp(goal, s, sizeof(s)) == 0)
{
return front;
}
int z;
for(z = 0; z < 9; z++)
{
if(!s[z]) break;
}
int x = z/3, y = z%3;
for(int d = 0; d < 4; d++)
{
int newx = x + dx[d];
int newy = y + dy[d];
int newz = newx * 3 + newy;
if(newx >= 0 && newx< 3 && newy >= 0 && newy < 3)
{
State& t = st[rear];
memcpy(&t, &s,sizeof(s));
t[newz] = s[z];
t[z] = s[newz];
dist[rear] = dist[front] + 1;
if(try_to_insert(rear))
{
rear++;
}
}
}
front++;
}
return 0;
}
int main()
{
for(int i = 0; i < 9; i++)
{
scanf("%d", &st[1][i]);
}
for(int i = 0; i < 9; i++)
{
scanf("%d", &goal[i]);
}
int ans = bfs();
if(ans > 0)
{
printf("%d\n", dist[ans]);
}
else
{
printf("-1\n");
}
return 0;
}
3. STL最好写,但是效率最低。使用STL集合,将每一个状态转换成9位的10进制整数,就可以用set<int>判重
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
typedef int State[9];
const int MAXSTATE = 1000000;
State st[MAXSTATE], goal;
int dist[MAXSTATE];
set<int> vis;
void init_lookup_table()
{
vis.clear();
}
int try_to_insert(int s)
{
int v = 0;
for(int i = 0; i < 9; i++)
{
v = v * 10 + st[s][i];
}
if(vis.count(v))
{
return 0;
}
vis.insert(v);
return 1;
}
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
int bfs()
{
init_lookup_table();
int front = 1, rear = 2;
while(front < rear)
{
State& s = st[front];
if(memcmp(goal, s, sizeof(s)) == 0)
{
return front;
}
int z;
for(z = 0; z < 9; z++)
{
if(!s[z])
{
break;
}
}
int x = z/3, y = z%3;
for(int d = 0; d < 4; d++)
{
int newx = x + dx[d];
int newy = y + dy[d];
int newz = newx * 3 + newy;
if(newx >= 0 && newx < 3 && newy >= 0&& newy < 3)
{
State& t = st[rear];
memcpy(&t, &s,sizeof(s));
t[newz] = s[z];
t[z] = s[newz];
dist[rear] = dist[front] + 1;
if(try_to_insert(rear))
{
rear++;
}
}
}
front++;
}
return 0;
}
int main()
{
for(int i = 0; i < 9; i++)
{
scanf("%d", &st[1][i]);
}
for(inti = 0; i < 9; i++)
{
scanf("%d", &goal[i]);
}
int ans = bfs();
if(ans > 0)
{
printf("%d\n", dist[ans]);
}
else
{
printf("-1\n");
}
return 0;
}