3*3的棋盘中摆放了0~8这9个数字,每次只能允许0与其上下左右相邻的4个位置进行交换,
使得棋盘最终达到一种目标状态,要求输出最少交换次数的过程。例如

   起始状态    目标状态1 2 3        1 2 3 6 0 4    =>  8 0 48 7 5        7 6 5

最少交换次数的变化过程如下

   1 2 3        1 2 3     1 2 3     1 2 3     1 2 3 6 0 4    =>  0 6 4  => 8 6 4  => 8 6 4  => 8 0 48 7 5        8 7 5     0 7 5     7 0 5     7 6 5

注:其中0也可以理解为空格,上下左右4格相邻格的数字可以移动到空格中。
要求:移动次数最少,并且输出每一步移动后棋盘的状态,请写出算法程序。

分析:
1.所有可能的状态数为9!=362880,
2.解空间树为4叉树(实际剪枝后多数节点的度为2和3),
深度可以达到100以上,回溯算法较难控制运算量

A*算法思路:
1.创建解空间树的根节点,并进入优先级队列Q
//Q中存储了搜索过程中的所有叶子结点

2.while(Q非空) {
队头元素e(节点)出队列, if(e达到问题解) { 输出解空间树中 e->…->根节点的路径信息。
break;//只找一个解时,则可以立即退出while循环 } else {
生成e的所有儿子节点,将通过剪枝函数的节点 进入队列Q; }
}
//从队列中出来的节点,即是解空间树中的非叶子节点,搜索过程中整个 //解空间树不能释放,否则会导致无法输出从根->目标节点的路径信息, //所以以下程序用向量v存储了解空间树中的所有节点。
//其中剪枝函数(也可叫限界函数)的基本控制逻辑如下:

*/

一个解,stl


#include<iostream>
#include<windows.h>
#include<math.h>
#include<conio.h>
#include<queue>
#include<vector>
using namespace std; struct node{int   step,len;//从根到当前节点状态走了step步,离目标至少len步 POINT d[9];//当前状态下数字0~9的行列号 int   chess[3][3];//当前状态对应的矩阵 struct node *parent;//指向当前节点的父节点
};
struct cmp{ //自定义比较运算符 bool operator()(node *a,node *b){return a->step+a->len > b->step+b->len;//step+len小的优先排队头 }
}; vector<node*>v;//v存树中所有节点的地址
priority_queue<node*,vector<node*>,cmp>Q;//Q存树中所有叶子节点
//Q的元素类型是指针型,并且确定优先级的比较函数cmp必须自定义实现。POINT obj[9]={{1,1},{0,0},{0,1},{0,2},{1,2},{2,2},{2,1},{2,0},{1,0}},//目标状态 offset[4]={{-1,0},{0,1},{1,0},{0,-1}};//4种移动的坐标偏移值
int  cnt=0;//解的移动步骤 void output(node*t)
{   if(t->parent) output(t->parent);cout<<endl<<cnt++<<"步:"<<endl;for(int i=0;i<3;i++){     for(int j=0;j<3;j++)  cout<<t->chess[i][j]<<" ";cout<<endl;}
}node* xianjie(node *p,int k)
{   node *q;int i=p->d[0].x+offset[k].x,j=p->d[0].y+offset[k].y,m;    if(i<0||i>2||j<0||j>2) return 0;q=new node(*p);m=p->chess[i][j]; q->d[0].x =i; q->d[0].y =j;q->d[m].x =p->d[0].x; q->d[m].y =p->d[0].y;          q->chess[p->d[0].x][p->d[0].y] = m;q->chess[i][j] = 0;q->step ++;q->len +=abs(p->d[0].x-obj[m].x)+abs(p->d[0].y-obj[m].y)-(abs(i-obj[m].x)+abs(j-obj[m].y)); q->parent = p;for(m=0;m<v.size();m++){for(i=0;i<9;i++)if(q->d[i].x!=v[m]->d[i].x || q->d[i].y!=v[m]->d[i].y) break;if(i==9) break;}if(m<v.size())//q节点已经存在 { if(q->len+q->step < v[m]->len+v[m]->step) {v[m]->step =  q->step;v[m]->len  =  q->len;v[m]->parent = p;}delete q; return 0;}else    return q;
}int isok(node* t)
{   for(int i=0;i<9;i++) if(t->d[i].x!=obj[i].x || t->d[i].y!=obj[i].y)  return 0;return 1;
}void Init()//创建并初始化根节点
{node *t=new node;int i,j,num;cout<<"初始数据:\n";t->len = 0;for(i=0;i<3;i++)for(j=0;j<3;j++){       cin>>num;t->chess[i][j]=num;t->d[num].x =i;t->d[num].y =j;if(num)t->len += abs(obj[num].x-i)+abs(obj[num].y-j);         }t->step = 0;t->parent =0;Q.push(t);//根节点t进入队列Q v.push_back(t);//根节点t进入向量v
}int main()
{node *p,*q;    Init();//初始化    while(!Q.empty()){p=Q.top();//获取队头节点   Q.pop();//删除队头节点 if(isok(p)) {output(p);//输出从根到p节点的变化过程 break;}for(int i=0;i<4;i++)//穷举p产生的所有儿子节点 if(q=xianjie(p,i))//有效的节点会在xianjie中计算其评估值h(q)  {                 //存放在q->len中 Q.push(q);//以q->step+q->len小优先插入优先级队列Q v.push_back(q);  }      }cout<<"\n访问节点数:"<<v.size();getch();for(int i=0;i<v.size();i++)delete v[i];return 0;
}

多个解,stl

#include<iostream>
#include<iomanip>
#include<windows.h>
#include<math.h>
#include<conio.h>
#include<queue>
#include<vector>
using namespace std; struct node{int   step,len;//从根到当前节点状态走了step步,离目标至少len步 POINT d[9];//当前状态下数字0~9的行列号 int   chess[3][3];//当前状态对应的矩阵 struct node *parent;//指向当前节点的父节点
};
struct cmp{ //自定义比较运算符 bool operator()(node *a,node *b){return a->step+a->len > b->step+b->len;//step+len小的优先排队头 }
}; vector<node*>v;//存树中所有节点的地址
priority_queue<node*,vector<node*>,cmp>Q;//存树中所有叶子节点
//Q的元素类型是指针型,并且确定优先级的比较函数cmp必须自定义实现。POINT obj[9]={{1,1},{0,0},{0,1},{0,2},{1,2},{2,2},{2,1},{2,0},{1,0}},//目标状态 offset[4]={{-1,0},{0,1},{1,0},{0,-1}};//4种移动的坐标偏移值
int  Min,c1,cnt=0;//解的移动步骤 void output(node*t)
{   if(t->parent) output(t->parent);cout<<endl<<setw(3)<<cnt++<<"步:";for(int i=0;i<3;i++){  if(i) cout<<"      ";for(int j=0;j<3;j++) cout<<setw(2)<<t->chess[i][j];cout<<endl;          }
}node* xianjie(node *p, int k, int status)
{   node *q;int i=p->d[0].x+offset[k].x,j=p->d[0].y+offset[k].y,m;    if(i<0||i>2||j<0||j>2) return 0;//q状态中0的行列号(i,j) q=new node(*p);m=p->chess[i][j]; q->d[0].x =i; //p变到q状态,只有数字0和m的位置有变化 q->d[0].y =j;q->d[m].x =p->d[0].x; q->d[m].y=p->d[0].y;            q->chess[p->d[0].x][p->d[0].y] = m;q->chess[i][j] = 0;q->step ++;q->len +=abs(p->d[0].x-obj[m].x)+abs(p->d[0].y-obj[m].y)-(abs(i-obj[m].x)+abs(j-obj[m].y)); q->parent = p;if(status==0){for(m=0;m<v.size();m++){for(i=0;i<9;i++)if(q->d[i].x!=v[m]->d[i].x || q->d[i].y!=v[m]->d[i].y) break;if(i==9) break;}if(m<v.size())//q节点已经存在 { if(q->len+q->step < v[m]->len+v[m]->step) {v[m]->step =  q->step;v[m]->len  =  q->len;v[m]->parent = p;}delete q; return 0;}//找一个解的剪枝逻辑}else{node *p1=p;for( ;p1;p1=p1->parent){for(i=0;i<9;i++)if(q->d[i].x!=p1->d[i].x || q->d[i].y!=p1->d[i].y) break;if(i==9) break;}if(p1 || q->step+q->len>Min) { delete q; return 0;}//找所有解的剪枝逻辑}   return q;
}int isok(node* t)
{   for(int i=0;i<9;i++) if(t->d[i].x!=obj[i].x || t->d[i].y!=obj[i].y)  return 0;return 1;
}void Init()//创建并初始化根节点
{node *t=new node;int i,j,num;cout<<"初始数据:\n";t->len = 0;for(i=0;i<3;i++)for(j=0;j<3;j++){       cin>>num;t->chess[i][j]=num;t->d[num].x =i;t->d[num].y =j;if(num) t->len += abs(obj[num].x-i)+abs(obj[num].y-j);        }t->step = 0;t->parent =0;Q.push(t);//根节点t进入队列Q v.push_back(t);//根节点t进入向量v
}void BFS()
{node *p,*q;    Init();//初始化    while(!Q.empty()){p=Q.top();//获取队头节点   Q.pop();//删除队头节点 if(p->len ==0) //isok(p){Min=p->step;output(p);//输出从根到p节点的变化过程 break;}for(int i=0;i<4;i++)//穷举p产生的所有儿子节点 if(q=xianjie(p,i,0))//有效的节点会在xianjie中计算其评估值h(x)  {                 // Q.push(q);  v.push_back(q);  }      }cout<<"\n访问节点数:"<<v.size();getch();while(!Q.empty())       Q.pop();p=v[0];//根节点保留,作为BFS1()计算的初始数据for(int i=1;i<v.size();i++)delete v[i];    v.clear() ; v.push_back(p);  Q.push(p);
}void BFS1()
{node *p,*q;    //根节点在BFS中最后两个语句已经 初始化cout<<"\n其它最优解:";if(Min==0) return ;//如果无解,直接结束   while(!Q.empty()){p=Q.top() ;Q.pop() ;if(p->len==0 && p->step==Min) //isok(p){cout<<"\n\n解"<<++c1;cnt=0;output(p);getch();//输出一个解,暂停 }else if(p->step + p->len <= Min)for(int i=0;i<4;i++)//穷举p产生的所有儿子节点 if((q=xianjie(p,i,1))!=NULL){                   Q.push(q);  v.push_back(q);  } }cout<<"\n访问节点数:"<<v.size();getch();while(Q.size())  Q.pop();for(int i=0;i<v.size();i++)delete v[i];v.clear() ;
}
int main()
{BFS();//找一个最优解的广度优先搜索 BFS1();//找所有最优解的广度优先搜索   return 0;
}

分支限界算法 之 A*算法(启发式搜索算法)---九宫重排游戏(也称八数码问题)相关推荐

  1. java九宫排序,九宫重排java

    (4) 启发式图搜索的 A*算法 九宫重排问题把估计函数f 定义为f 例:九宫重排问题把估计函数f(x)定义为f )=d)+w(n)=d(n)+w(n) 其中d 表示节点深度; ...... 最长距离 ...

  2. [算法分析与设计]拼图问题或八数码问题(搜索算法)

    [问题分析]: 拼图游戏实际上是八数码问题的小变形或者说其实就是八数码问题,针对八数码问题我们有三种搜索算法,分别是宽度优先搜索算法,深度优先搜索算法,启发式搜索算法A*.在解决路径类问题(即在一个二 ...

  3. 你必须会的启发式搜索算法--A*算法

    一.算法原理 A* 算法,就是解决在一个平面 grid地图中寻找起点到终点的最短路径问题的算法,类似于Dijkstra算法和BFS算法一样,属于广度优先搜索.实际上它还是一个启发式搜索算法,什么叫启发 ...

  4. 经典算法研究系列:八、再谈启发式搜索算法

     经典算法研究系列:八.再谈启发式搜索算法 作者:July   二零一一年二月十日 本文参考: I.  维基百科. II. 人工智能-09 启发式搜索. III.本BLOG内,经典算法研究系列:一.A ...

  5. 启发式搜索算法(A*算法)

    A算发:在bfs算法中,若对每个状态n都设定估价函数f(n)=g(n)+h(n),并且每次从开启列表中选节点 进行扩展时,都选取f值最小的节点,则该搜索算法为启发式搜索算法,又称A算法. g(n):从 ...

  6. [小明学算法]3.启发式搜索算法----A*算法之我见

    1.算法介绍 a*算法是一种寻路算法,类似于图的广度优先遍历搜索. 2.基本概念 设计先两个集合CloseList和OpenList,CloseList表示已经走过的节点,OpenList中的节点表示 ...

  7. 基于python的九宫重排问题的启发式搜索(A*算法)求解程序

    基于python的九宫重排问题的启发式搜索(A*算法)求解程序 一.实验内容 在3х3组成的九宫棋盘上,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布 ...

  8. 数据结构和算法 第二小题 九宫重排(1)

    参考了csdn里的一篇文章(leetcode上没找到),他用C++写的, 双向bfs队列搜索.我把C++的代码改成了java,链接:https://blog.csdn.net/qq_45775045/ ...

  9. 分支限界发:Dijkstra算法

    从分支限界的角度来看Dijkstra算法: Dijkstra算法是基于贪心的广度优先搜索,也可以看成分支限界法,从分支限界的角度来看,Dijkstra算法看起来就更加清晰明了 代码实现: # ==== ...

最新文章

  1. DOM操作表格的各种属性[z]
  2. resetroot_169route_python2(用于ubuntu12.04和14.04,centos系列)
  3. 查询大于2分钟的数据
  4. ajax.actionlink使用问题
  5. 腾讯或于本周正式宣布合并搜狗?官方回应:看点招聘及搜狗合并均正常进行...
  6. php 发送网络命令,linux命令经典用法与配置收录
  7. 人工神经网络方法学习步长_人工神经网络-一种直观的方法第1部分
  8. uniapp小程序解压压缩包 (使用jszip)
  9. vim插件配置安装与分享
  10. 论坛刷访客神器-Header自定义工具
  11. ChucK初步(8)
  12. 网站快速成型工具-Element UI
  13. 使用 GVM 工具管理 Go 版本
  14. 如何优雅的完成一场说来就来的APP自建
  15. mysql数据库的配置
  16. 【安卓】[Android]添加google账户 - 您的用户名和密码不匹配,请重试
  17. 7-46 请输出郑州轻工业大学OJ平台网址
  18. Servlet 入门
  19. 03-Java解决应用程序被安全阻止
  20. runtime(四) method swizzling 与AOP编程

热门文章

  1. AttributeError: module 'easygui' has no attribute 'msgbox'错误
  2. elment ui 修改时间选择器的宽度及高度
  3. 输出一个由*组成的三角形图案_一文带你读懂集成电路的组成与封装形式
  4. sparksql中大小表jion
  5. itk和c++读取dicom序列
  6. 今有兽,六首四足;禽,四首二足,上有七十六首,下有四十六足。问:禽、兽各几何?...
  7. 文兄的算法题——算术表达式递归构造二叉树
  8. 利用Java通过阿里云对图片进行内容审核
  9. APS的主要功能有哪些?你了解吗?
  10. 实习技术员的基本功(二)