java宽度优先搜索之八数码,广度优先搜索解决八数码问题

八数码简介

八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字,不同棋子上标的数字不同样。棋盘上另一个空格,与空格相邻的棋子能够移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。

求解八数码问题要懂得的知识

1.康托展开,八数码在交换的过程中状态会改变,康托展开用于求出某一格局的状态数。唐托展开公式:X=a[n](n-1)!+a[n-1](n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]

2.逆序数,通过求初始格局和目标格局逆序数,然后在比较两者的逆序数的奇偶性是否相同,如果奇偶性相同,则可以从初始格局变到目标格局。否则,不可达。逆序数求法:假设有n个元素的排列,a[i]为排列中的元素(0<=i

3.在八数码中0位置的数与它相邻的上下左右的位置的数交换不会影响这个格局的逆序数的奇偶性。比如有以下格局:

4

6

7

5

8

1

2

3

0

如果2这个数和5这个数交换就会导致格局的逆序数的奇偶性的变化,而如果0和1交换就不会导致奇偶性的变化。我们要保证移动前和移动后逆序数的奇偶性不改变,用一维数组来存储格局,要注意索引。

广度优先搜索求解八数码问题代码

#include

#include

#include

#include

#include

#include

#include

using namespace std;

#define N 9

int jc[N+1]={1,1,2,6,24,120,720,5040,40320,362880};//0-9的阶乘

typedef struct data

{

int arr[N];//格局

int hash;//存储某一格局的哈希

int pos;//0当前位置

int step;//记录步数

}Node;

int dir[4][2]={

{0,1},

{1,0},

{0,-1},

{-1,0}

};

/**

* 康托展开

*/

int cantor(int arr[N])

{

int i,j;

int sum=0;

for( i=0;i

{

int nmin=0;

for(j=i+1;j

{

if(arr[i]>arr[j])

nmin++;

}

sum+=(nmin*jc[N-i-1]);

}

return sum;

}

/**

*数据交换

*/

void swap(int *arr,int i,int j)

{

int t=arr[i];

arr[i]=arr[j];

arr[j]=t;

}

/**

* 打印数组,测试用

*/

void printArray(int * arr)

{

int i,j;

for (i=0;i

{

if(i%3==0)

cout<

cout<

}

cout<

}

/**

* 复制数组

*/

void copyArray(int src[N],int target[N])

{

int i;

for(i=0;i

{

target[i]=src[i];

}

}

/**

* 广搜

*/

int bfs(int arr[N],int sHash,int tHash)

{

if(sHash==tHash)

return 0;

int i,j;

queue q;

set setHash;

Node now,next;

copyArray(arr,now.arr);

int pos=0;

for (i=0;i

{

if(arr[i]==0)

break;

pos++;

}

now.hash=sHash;

now.step=0;

next.step=0;

now.pos=pos;

q.push(now);

setHash.insert(now.hash);

while(!q.empty())

{

now=q.front();

q.pop();

for (i=0;i<4;i++)

{

int offsetX=0,offsetY=0;

offsetX=(now.pos%3+dir[i][0]);

offsetY=(now.pos/3+dir[i][1]);

if(offsetX>=0&&offsetX<3&&offsetY<3&&offsetY>=0)

{

copyArray(now.arr,next.arr);//每次换方向,就复制

next.step=now.step;

next.step++;

swap(next.arr,now.pos,offsetY*3+offsetX);

next.hash=cantor(next.arr);

next.pos=(offsetY*3+offsetX);

int begin=setHash.size();

setHash.insert(next.hash);

int end=setHash.size();

if(next.hash==tHash){

return next.step;

}

if(end>begin)

{

q.push(next);

}

}

}

}

return -1;

}

/**

*求逆序数

*/

int inversion(int arr[N])

{

int sum=0;

for(int i=0;i

{

for(int j=i+1;j

{

if(arr[j]

{

sum++;

}

}

}

return sum;

}

int main(int argc, char **argv)

{

int i,j;

string s="123456780";

string t="123456078";

int is[N],it[N];//源int数组和目标int数组

for (i=0;i<9;i++)

{

if (s.at(i)>='0'&&s.at(i)<='8')

{

is[i]=s.at(i)-'0';

}else

{

is[i]=0;

}

if (t.at(i)>='0'&&t.at(i)<='8')

{

it[i]=t.at(i)-'0';

}else

{

it[i]=0;

}

}

int sHash,tHash;//源哈希和目标哈希

sHash=cantor(is);

tHash=cantor(it);

int inver1=inversion(is);//求初始格局的逆序数

int inver2=inversion(it);//求目标格局的逆序数

if((inver1+inver2)%2==0)

{

int step=bfs(is,sHash,tHash);

cout<

}

else

{

cout<

}

return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值