3*3的棋盘中摆放了0~8这9个数字,每次只能允许0与其上下左右相邻的4个位置进行交换,
使得棋盘最终达到一种目标状态,要求输出最少交换次数的过程。例如起始状态 目标状态 1 2 3 1 2 3 6 0 4 => 8 0 4 8 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 4 8 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
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
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;
}