八数码问题的A*算法实现

问题描述

八数码问题也称为九宫问题。在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
*/
include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置()"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置()"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一图中与目标图有多少个元素不相同,queue[i]为当前图与目标图有多少个元素不相同通过这两个数的比较,就可以得出当前图较之上一图向目标图接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值