【宽搜入门】8数码难题

网上看到很多形形色色的解决方法,感觉自己挺与众不同的,来分享一下。

以下是原题:

题目描述

 

初始状态的步数就算1,哈哈

输入:第一个3*3的矩阵是原始状态,第二个3*3的矩阵是目标状态。 
输出:移动所用最少的步数 

Input

2 8 3 
1 6 4 
7 0 5 
1 2 3 
8 0 4 
7 6 5 

Output

6

-----------------------------------------------------------  分割线 --------------------------------------------------------- 

题目介绍的含糊不清,大致意思是:在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。(和小时候手机上玩的拼图差不多)

解决方法是用宽度优先搜索,宽度优先搜索的万能模板如下(出自算法笔记):

void BFS(int s){
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        取出队首元素top;
        访问队首元素top;
        将队首元素出队;
        将top的下一层结点中未曾入队的结点全部入队,并设置为已入队;
    }
}

接下来,上代码:

------------------------------------------分割线---------------------------------------------------

解释一下结构体变量的state以及judge函数中的(i-state):

当矩阵空位进行一次移动后,为了防止下一次移动又返回原样,如:
     2 8 3         2 8 3             2 8 3 
    1 6 4   —>  1 0 4    —>  1 6 4 
    7 0 5          7 6 5             7 0 5
    第一次进行上移操作,第二次进行下移操作又使矩阵恢复成原装,如此反复进行造成死循环
    所以需要记录位移操作,很多方法都用map,数组等,笔者的方法可能不是很好,如下: 
     第一次做了上移第二次就不能下移,上下和左右是相排斥的。
     四个位置的移动i=0,1,2,3分别代表上、左、下、右上和下为0,2左和右为1,3,两者差皆为绝对值2
     用state记录当前移动的参数i,在下一次就行移动时,判断绝对值(i-state)是否为2,为2则不合法。 

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;

//定义代表当前矩阵 
struct Node{                  
	int a[3][3];        //矩阵的元素储存 
	int y;				//(y,x)为空位0的坐标 
	int x;
	int step;			//step为当前移动的步数 
	int state;			//state为当前矩阵的移动状态(具体看如下使用) 
}node;

int b[3][3];			//二维数组b为目标矩阵 
int t1[4]={-1,0,1,0};
int t2[5]={0,-1,0,1};	//0~4代表上左下右四个位置的移动 

//判断当前结点的移动是否合法
bool judge(int y,int x,int i,int state){	
	if(y>2||y<0||x>2||x<0)
		return false;
	if(abs(i-state)==2)
		return false;
	return true;
}

//交换矩阵中0与其相邻位置的数字,视为移动 
void exchange(int &a,int &b){
	int temp;
	temp=a;
	a=b;
	b=temp;
}

//判断当前矩阵与目标矩阵b是否相等 
bool equal(int a[3][3],int b[3][3]){
	int i,j;
	for(i=0;i<3;i++)
		for(j=0;j<3;j++){
			if(a[i][j]!=b[i][j])
				return false;
		} 
	return true;
}


void BFS(){
	queue<Node> q;
	q.push(node);
	while(!q.empty()){         //只要队列不空,就说明还有元素可以移动 
		node=q.front();		   //取出队首元素赋值给node当前矩阵,访问node 
		q.pop();
		
		if(equal(node.a,b))	   //第一次出现当前矩阵与目标矩阵相等的情况,就是最短步数 
		{
			printf("%d\n",node.step);		
			return;
		}
		
		for(int i=0;i<4;i++){	//对当前矩阵的空位0进行上下左右的移动,得到新的空位结点 
			int newY=node.y+t1[i];
			int newX=node.x+t2[i];
			if(judge(newY,newX,i,node.state)){		//判断当前移动是否合法 
				Node t=node;
				exchange(t.a[node.y][node.x],t.a[newY][newX]);   //进行结点交换(移动) 
				t.y=newY;
				t.x=newX;
				t.step=node.step+1;	//步数加1 
				t.state=i;			//记录当前移动状态 
				q.push(t);										
			}
		}
	}
}

int main(){
	int i,j;
	for(i=0;i<3;i++)			//输入原始矩阵,储存到node 
		for(j=0;j<3;j++){
			scanf("%d",&node.a[i][j]);
			if(node.a[i][j]==0){	//如果输入的是0,则保存空点的状态		
				node.state=10;
				node.y=i;
				node.x=j;
				node.step=1;
			}
		}

	for(i=0;i<3;i++)			//输入目标矩阵 
		for(j=0;j<3;j++)
			scanf("%d",&b[i][j]);   
	BFS();
	return 0;
		
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值