问题描述
八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
代码实现
/*
AIDreamer
2017/5/27
*/
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<queue>
using namespace std;
const int MAXNODE=100000;
const int INF=999999999;
const int MOD=10007;
struct NODE
{
int state;
int blank;//blank表示0在state的第几位数字之后
int f;//估价函数
int g;//从起始节点到目前的步数
int h;//启发式函数,目前节点和目标节点之间的距离,用欧几里得距离计算
int pre;//该节点的父亲节点
int loc;
bool operator < (const NODE &rhs)const {
return f>rhs.f;
}
}node[MAXNODE];
bool CLOSE[MAXNODE];
int nodecount=0;
//hash表用来保存访问过的状态
struct HASH
{
int state[100];//下标从1开始,保存状态
int state_loc[100];//保存状态所对应的下标
int cnt;
}Hash[MAXNODE];
void Put_into_hash(int x)//将node[x]存到hash表中
{
int loc=node[x].state%MOD;
Hash[loc].state[++Hash[loc].cnt]=node[x].state;
Hash[loc].state[Hash[loc].cnt]=x;
}
int Is_in_hash(struct NODE a)//若在hash表中返回状态在node中的下标,若不在返回-1
{
int loc=a.state%MOD;
for(int i=1;i<=Hash[loc].cnt;i++)
{
if(a.state==Hash[loc].state[i])return Hash[loc].state_loc[i];
}
return -1;
}
NODE init_node;
NODE target_node;
/*
得到起点和终点的state
*/
void In()
{
int a[10];//保存3*3的格子
int state=0;
int blank;
int ans,t;
//得到init_node
ans=0;
for(int i=0;i<9;i++)
{
scanf("%d",&t);
if(t==0){init_node.blank=i;continue;}
ans=ans*10+t;
}
init_node.state=ans;
//得到target_node
ans=0;
for(int i=0;i<9;i++)
{
scanf("%d",&t);
if(t==0){target_node.blank=i;continue;}
ans=ans*10+t;
}
target_node.state=ans;
}
int Get_euclidean_distance(struct NODE aa,struct NODE bb)
{
//先将int型的状态转换到数组中
int a[10];
int b[10];
int a_state=aa.state;
int b_state=bb.state;
for(int i=8;i>=0;i--)
{
if(aa.blank==i){a[i]=0;continue;}
a[i]=a_state%10;
a_state/=10;
}
for(int i=8;i>=0;i--)
{
if(bb.blank==i){b[i]=0;continue;}
b[i]=b_state%10;
b_state/=10;
}
//计算两个状态之间的距离
int sum_dis=0;
int t;//临时变量
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
if(a[i]==b[j] && a[i]!=0)
{
t=abs(i-j);
sum_dis+=t%3+t/3;
}
}
}
return sum_dis;
}
void Init()
{
init_node.g=0;
init_node.h=Get_euclidean_distance(init_node,target_node);
init_node.f=init_node.g+init_node.h;
init_node.pre=0;
target_node.f=INF;
target_node.pre=0;
}
void Change_state(struct NODE &node,int blank_loc,int num_loc)//改变node的状态,将状态中blank_loc和num_loc的值互换
{
int s[10];
int node_state=node.state;
for(int i=8;i>=0;i--)
{
if(node.blank==i){s[i]=0;continue;}
s[i]=node_state%10;
node_state/=10;
}
s[blank_loc]=s[num_loc];
s[num_loc]=0;
node.blank=num_loc;
node.state=0;
for(int i=0;i<9;i++)
{
if(s[i]!=0)node.state=node.state*10+s[i];
}
return ;
}
void Exchange_state(struct NODE &node1,struct NODE &node2)//交换node1状态和node2状态
{
struct NODE temp_node;
temp_node=node1;
node1=node2;
node2=temp_node;
}
void Print_node(struct NODE x)//将状态以3*3的形式输出
{
int state=x.state;
int blank=x.blank;
int s[10];
for(int i=8;i>=0;i--)
{
if(i==blank){s[i]=0;continue;}
s[i]=state%10;
state/=10;
}
for(int i=0;i<9;i++)
{
cout<<s[i]<<" ";
if(i%3==2)cout<<endl;
}
}
void A_Star()//找到最优方案后更新target_node中的pre值
{
node[++nodecount]=init_node;
node[nodecount].loc=1;
priority_queue<NODE> Q;
Q.push(node[1]);//将初始状态入队
Put_into_hash(1);
while(!Q.empty())
{
struct NODE x=Q.top();
if(x.state==target_node.state && x.blank==target_node.blank){ target_node=x;break;}
Q.pop();
NODE temp=x;//临时结构体变量
for(int i=0;i<9;i++)
{
temp=x;
if( (abs(temp.blank-i)/3+abs(temp.blank-i)%3) ==1 )
{
Change_state(temp,temp.blank,i);
//cout<<"after_change: "<<temp.state<<" "<<temp.blank<<endl;
temp.g=x.g+1;
temp.h=Get_euclidean_distance(temp,target_node);
temp.f=temp.g+temp.h;
int loc=Is_in_hash(temp);
if(loc==-1)//若不在hash表中则加入到hash表
{
node[++nodecount]=temp;
node[nodecount].loc=nodecount;
Put_into_hash(nodecount);
node[nodecount].pre=x.loc;
Q.push(node[nodecount]);
}
else
{
if(CLOSE[loc]!=1)//在OPEN表中
{
if(temp.f<node[loc].f)
{
node[loc].f=temp.f;node[loc].g=temp.g;node[loc].h=temp.h;
node[loc].pre=x.loc;
}
}
else //节点在CLOSE表中
{
if(temp.f<node[loc].f)
{
node[loc].f=temp.f;node[loc].g=temp.g;node[loc].h=temp.h;
node[loc].pre=x.loc;
Q.push(node[loc]);//将该节点加入到OPEN表中
CLOSE[loc]=0;
}
}
}
}
}
CLOSE[x.loc]=1;
}
}
int main()
{
In();
//Exchange_state(init_node,target_node);//为了方面最后输出,交换初始和目标状态
Init();
cout<<"初始状态: "<<init_node.state<<" 空格位置: "<<init_node.blank<<endl;
cout<<"目标状态: "<<target_node.state<<" 空格位置: "<<target_node.blank<<endl;
A_Star();
int loc=target_node.loc;
int step=0;
while(loc!=0)
{
cout<<"----------第"<<step++<<"步:----------"<<endl;
Print_node(node[loc]);
loc=node[loc].pre;
}
cout<<endl;
cout<<"一共需要"<<step-1<<"步!"<<endl;
return 0;
}
/*
1 0 3
7 2 4
6 8 5
1 2 3
8 0 4
7 6 5
*/