2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
31
#include
#include
#include
#define MAXSIZE 1000000
typedef int State[9];
/*
s的数据类型是长度为100的数组,数组元素s[i]的数据类型是State是长度为10的数组,等同于定义了一个二维数组s[100]=iarr[100][9].
int iarr[100][9],arr的数据类型是长度为100的数组,数组元素是arr[i],arr[i]的数据类型是长度为10的数组.
*/
State st[MAXSIZE];//状态数一定要多定义,否则一不小心就超了
State goal;
int dist[MAXSIZE];//距离数组
int go[][2] =//上下左右方向的数组
{
{-1,0},{
1,0},{
0,-1},{
0,1}};
int iVis[362880],fact[9];//9!=362880,8!=40320,9*8!=9!共有这么多排序,然后我们寻找,我们初始化fact
//节点查找表
void init_lookup_table()//初始化查找表
{
fact[0] = 1;
for(int i = 1 ; i < 9; i++)
{fact[i] = fact[i-1]*i;}
//fact[0]=1,fact[1]=1!,fact[2]=2!...fact[8]=8!
}
//bool isInsert(State state)
int try_to_insert(int n)
//去重,采用编码与解码机制,确保一个9维状态只能映射到一个数字,并且映射的数字最大值不能超过9!
{
int iCode = 0;//编码值
for(int i = 0 ; i < 9 ; i++)
{
int iCnt = 0;
for(int j = i+1; j < 9;j++)
{
if(st[n-1][j] < st[n-1][i])//统计每个排列中,后面小于前面排列的数字个数
{ iCnt++;}
}
iCode += fact[8-i]*iCnt;
}
if(iVis[iCode])//如果已经访问过
return 0;
else{
return iVis[iCode] = 1;//同时完成赋值和返回值操作
}
}
int bfs()//宽搜,队列队列队列。
{
int rear = 2,front = 1;
init_lookup_table();//初始化。这里进行判重,对于树不需要判断重复,但是对于图需要判断。
while(front < rear)
{
State& state = st[front];
/*
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符 &引用名=目标变量名;
例如:int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名
*/
if(memcmp(goal,state,sizeof(state)) == 0)
{
//判断是否找到的工作要放在开头,找到返回
return front;
}
int iZ,iX,iY;
for(int i = 0 ; i < 9; i++)//确定0所在的位置
{
if(!state[i])
{ iZ = i;
iX=iZ/3;iY=iZ%3;
break;//凡是寻找类的问题,一旦找到,必须用break跳出
}
}
//生成下一步位置
int newz,newx,newy;
for(int i = 0; i < 4; i++)
{
newx = go[i][0] + iX;
newy = go[i][1] + iY;
newz = newx*3 + newy;//确定0的新位置
if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3)//剪枝
{
State& newState = st[rear];
//这里应该从队尾提前将原来老的状态拷贝给新的状态,再将新状态中需要修改0元素的地方进行修改,需要用引用,为修改做准备
memcpy(&newState,&state,sizeof(state));
newState[newz] = state[iZ];//新矩阵0元素的位置上放0元素
newState[iZ] = state[newz];//新矩阵原来放0元素的位置上现在放上新生成的0元素的坐标,这里必须用原来被交换元素的值替换
dist[rear] = dist[front] + 1;//更新移动的步数
}
if(try_to_insert(rear))//修改队尾指针
{
rear++;
}
}
front++;//不管是否成功,修改队头
}
return 0;
}
void process()
{
//初始化队头和队尾元素
for(int i = 0 ; i < 9;i++)
{
scanf("%d",&st[1][i]);
}
for(int j = 0 ; j < 9; j++)
{
scanf("%d",&goal[j]);
}
dist[1] = 0;//设置第一步移动的距离为0
memset(iVis,0,sizeof(iVis));//初始化访问内存块,就是这句话没加导致错误的
}
int main(int argc,char* argv[])
{
process();
int ans = bfs();
//返回的是front的值,但不是移动次数,移动次数得用dist来计算,因为这里是宽度优先搜索,如果用front的值,那么中间尝试的节点也算了
if(ans > 0)
printf("%d\n",dist[ans]);
else printf("-1\n");
return 0;
}
注意引用的涵义,一开始不知道就很奇怪,那个st[rear]是如何赋值的。
#include
#include
#include
typedef int State[9];
State st[MAXSIZE];//状态数一定要多定义,否则一不小心就超了
State goal;
int dist[MAXSIZE];//距离数组
int go[][2] =//上下左右方向的数组
{
{-1,0},{
1,0},{
0,-1},{
0,1}};
int iVis[362880],fact[9];//9!=362880,8!=40320,9*8!=9!共有这么多排序,然后我们寻找,我们初始化fact
//修改处
#define MAXSIZE 1000000
int head[MAXSIZE],next[MAXSIZE];
void init_lookup_table()//初始化查找表
{
memset(head,0,sizeof(head));
}
int hash(State &s){
int v=0;
for(int i=0;i<9;i++) v=v*10+s[i];
//随便算,例如把9个数字组合成9位数
return v%MAXSIZE;
}
int try_to_insert(int s)
{
int h=hash(st[s]);//求第s个状态的hash值
int u=head[h];//从表头开始查找链表
while(u){
//这个while的作用是遍历具有相同哈希值的一条链,如果能找到,说明插入失败,如果找不到,则采用头插法插入新来的节点
if(memcmp(st[u],st[s],sizeof(st[s]))==0)//找到了,插入失败
return 0;
u=next[u];//竖着链表继续查找
}
next[s]=head[h];//插入到链表中
head[h]=s;
return 1;
}
int bfs()//宽搜,队列队列队列。
{
int rear = 2,front = 1;
init_lookup_table();//初始化。这里进行判重,对于树不需要判断重复,但是对于图需要判断。
while(front < rear)
{
State& state = st[front];
/*
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符 &引用名=目标变量名;
例如:int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名
*/
if(memcmp(goal,state,sizeof(state)) == 0)
{
//判断是否找到的工作要放在开头,找到返回
return front;
}
int iZ,iX,iY;
for(int i = 0 ; i < 9; i++)//确定0所在的位置
{
if(!state[i])
{ iZ = i;
iX=iZ/3;iY=iZ%3;
break;//凡是寻找类的问题,一旦找到,必须用break跳出
}
}
//生成下一步位置
int newz,newx,newy;
for(int i = 0; i < 4; i++)
{
newx = go[i][0] + iX;
newy = go[i][1] + iY;
newz = newx*3 + newy;//确定0的新位置
if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3)//剪枝
{
State& newState = st[rear];
//这里应该从队尾提前将原来老的状态拷贝给新的状态,再将新状态中需要修改0元素的地方进行修改,需要用引用,为修改做准备
memcpy(&newState,&state,sizeof(state));
newState[newz] = state[iZ];//新矩阵0元素的位置上放0元素
newState[iZ] = state[newz];//新矩阵原来放0元素的位置上现在放上新生成的0元素的坐标,这里必须用原来被交换元素的值替换
dist[rear] = dist[front] + 1;//更新移动的步数
}
if(try_to_insert(rear))//修改队尾指针
{
rear++;
}
}
front++;//不管是否成功,修改队头
}
return 0;
}
void process()
{
//初始化队头和队尾元素
for(int i = 0 ; i < 9;i++)
{
scanf("%d",&st[1][i]);
}
for(int j = 0 ; j < 9; j++)
{
scanf("%d",&goal[j]);
}
dist[1] = 0;//设置第一步移动的距离为0
memset(iVis,0,sizeof(iVis));//初始化访问内存块,就是这句话没加导致错误的
}
int main(int argc,char* argv[])
{
process();
int ans = bfs();
//返回的是front的值,但不是移动次数,移动次数得用dist来计算,因为这里是宽度优先搜索,如果用front的值,那么中间尝试的节点也算了
if(ans > 0)
printf("%d\n",dist[ans]);
else printf("-1\n");
return 0;
}
别人家 set 头文件的整理
#include
#include
#include
#include
#include
using namespace std;
#define MAXSIZE 1000000
typedef int State[9];
State st[MAXSIZE];//状态数一定要多定义,否则一不小心就超了
State goal;
int dist[MAXSIZE];//距离数组
int go[][2] =//上下左右方向的数组
{
{-1,0},{
1,0},{
0,-1},{
0,1}};
int iVis[362880],fact[9];//9!=362880,8!=40320,9*8!=9!共有这么多排序,然后我们寻找,我们初始化fact
//修改处
set<int> vis;
void init_lookup_table()//初始化查找表
{
vis.clear();
}
int try_to_insert(int s)
{
int v=0;
for(int i=0;i<9;i++) v=v*10+st[s][i];
if(vis.count(v)) return 0; //判断是否在容器中。是,返回错误。
vis.insert(v);
return 1;
}
/*另一种方式,声明一个结构体,并重载“括号运算”来比较两个状态。
struct cmp{
bool operator()(int a,int b) const{//重新定义 a vis;
void init_lookup_table(){vis.clear();}
int try_to_insert(int s){
if(vis.count(s)) return 0;
vis.insert(s);
return 1;
}
*/
int bfs()//宽搜,队列队列队列。
{
int rear = 2,front = 1;
init_lookup_table();//初始化。这里进行判重,对于树不需要判断重复,但是对于图需要判断。
while(front < rear)
{
State& state = st[front];
if(memcmp(goal,state,sizeof(state)) == 0)
{
//判断是否找到的工作要放在开头,找到返回
return front;
}
int iZ,iX,iY;
for(int i = 0 ; i < 9; i++)//确定0所在的位置
{
if(!state[i])
{ iZ = i;
iX=iZ/3;iY=iZ%3;
break;//凡是寻找类的问题,一旦找到,必须用break跳出
}
}
//生成下一步位置
int newz,newx,newy;
for(int i = 0; i < 4; i++)
{
newx = go[i][0] + iX;
newy = go[i][1] + iY;
newz = newx*3 + newy;//确定0的新位置
if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3)//剪枝
{
State& newState = st[rear];
//这里应该从队尾提前将原来老的状态拷贝给新的状态,再将新状态中需要修改0元素的地方进行修改,需要用引用,为修改做准备
memcpy(&newState,&state,sizeof(state));
newState[newz] = state[iZ];//新矩阵0元素的位置上放0元素
newState[iZ] = state[newz];//新矩阵原来放0元素的位置上现在放上新生成的0元素的坐标,这里必须用原来被交换元素的值替换
dist[rear] = dist[front] + 1;//更新移动的步数
}
if(try_to_insert(rear))//修改队尾指针
{
rear++;
}
}
front++;//不管是否成功,修改队头
}
return 0;
}
void process()
{
//初始化队头和队尾元素
for(int i = 0 ; i < 9;i++)
{
scanf("%d",&st[1][i]);
}
for(int j = 0 ; j < 9; j++)
{
scanf("%d",&goal[j]);
}
dist[1] = 0;//设置第一步移动的距离为0
memset(iVis,0,sizeof(iVis));//初始化访问内存块,就是这句话没加导致错误的
}
int main(int argc,char* argv[])
{
process();
int ans = bfs();
//返回的是front的值,但不是移动次数,移动次数得用dist来计算,因为这里是宽度优先搜索,如果用front的值,那么中间尝试的节点也算了
if(ans > 0)
printf("%d\n",dist[ans]);
else printf("-1\n");
return 0;
}