3.3 8 数码问题
【问 题 描 述 】用最 少的 步 数 实现( 空 格 用 0 表 示):
(1) BFS
typedef int state[9];
const int N=1000000;
state st[N],goal;
int dist[N],parent[N];
const int dx[]={-1,1,0,0}, dy[]={0,0,-1,1};
void print(int p)
{
if (parent[p]!=-1) print(parent[p]);
// 输出st[p]
state &s=st[p];
cout<<s[0]<<" "<<s[1]<<" "<<s[2]<<endl;
cout<<s[3]<<" "<<s[4]<<" "<<s[5]<<endl;
cout<<s[6]<<" "<<s[7]<<" "<<s[8]<<endl;
cout<<endl;
}
int BFS() // 返回目标状态在st[]中的下标
{
init_lookup_table(); // 初始化查找表(判重用)
int front=1, rear=2;
parent[front]=-1;
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]==0) break; // 找“零”
int x=z/3, y=z%3; // “0”的行列编号
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;
parent[rear]=front;
if (try_to_insert(rear)) rear++;
}
}
front++;
}
return 0;
}
(2) 顺序化 *
下面的 代 码 把 0~ 8 的 全 排列 和 0~ 362879( 9 ! -1 ) 的 整数 一 一对 应 起 来。
bool visited[362880];
int fact[9];
void init_lookup_table()
{
fact[0]=1;
for (int i=1;i<9;i++) fact[i]=fact[i-1]*i;
}
bool try_to_insert(int s)
{
int code=0; // 把st[s]映射到整数code
state &p = st[s];
for (int i=0; i<9; i++)
{
int cnt=0;
for (int j=i+1; j<9; j++)
if (p[j]<p[i])
code+=fact[8-i]*cnt;
}
if (visited[code])
return false;
else
return visited[code]=true;
}
(3) 使用 哈希表判重
上面 的 编 码 法 时 间效率 很高 , 但是 毕 竟 不 是通 用的方法 。如果 结点数 非 常大,编码 也是无法 承受的 。
const int MAXSIZE = 1000003;
int head[MAXSIZE], next[MAXSIZE];
void init_lookup_table() {memset(head,0,sizeof(head));}
int h(state &s)
{
// 散列函数:把每个格子里数连接成一个九位数,并将它除以MAXSIZE的余数作为哈希函数值。
int v=0;
for (int i=0; i<9; i++) v=v*10+s[i];
return v%MAXSIZE;
}
bool 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 false;
u=next[u];
}
next[s]=head[h];
head[h]=s;
return true;
}