八数码问题算法,谁有?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八数码问题算法,谁有?相关的知识,希望对你有一定的参考价值。

我要算法,不要代码
广度优先或者深度优先等都行。

广度优先搜索法
在搜索法中,广度优先搜索法是寻找最短路经的首选。
1.广度优先搜索算法的基本步骤
1)建立一个队列,将初始结点入队,并设置队列头和尾指针
2)取出队列头(头指针所指)的结点进行扩展,从它扩展出子结点,并将这些结点按扩展的顺序加入队列。
3)如果扩展出的新结点与队列中的结点重复,则抛弃新结点,跳至第六步。
4)如果扩展出的新结点与队列中的结点不重复,则记录其父结点,并将它加入队列,更新队列尾指针。
5)如果扩展出的结点是目标结点,则输出路径,程序结束。否则继续下一步。
6)如果队列头的结点还可以扩展,直接返回第二步。否则将队列头指针指向下一结点,再返回第二步。
2.搜索路径的输出
搜索到目标结点后,需要输出搜索的路径。每个结点有一个数据域last,它记录了结点的父结点,因此输出搜索路径时,就是从目标结点Q出发,根据last找到它的父结点,再根据这个结点的last找到它的父结点,....,最后找到初始结点。搜索的路径就是从初始结点循相反方向到达目标结点的路径。
参考技术A #include<stdio.h>
#include<stdlib.h>
#define N 3 //N阶矩阵
typedef struct node

int place[N][N];
struct node* mum,*next;
struct node* son0, *son1,* son2,* son3;
bitree;
typedef struct

bitree* front,*rear;
LinkQueue;
void Assign(bitree*,bitree*);
bitree* SolveTree(bitree*,bitree*);
bitree* CreBit();
LinkQueue* InitQueue();
void move(bitree*,int);
void EnterQueue(LinkQueue* ,bitree* );
bitree* Dequeue(LinkQueue*);
int Judge(bitree*,bitree*);
void invert(bitree*);
void Printf(bitree*);
int Search(LinkQueue* ,bitree* );
int Unorder(int *);
int AnsJud(bitree* ,bitree* );
void main()

int i;
bitree* root=CreBit();
bitree* target=CreBit();
bitree* ans,* head;
printf("输入初始数码\n");
for(i=0;i<N;i++)

scanf("%d %d %d",&(root->place[i][0]),&(root->place[i][1]),&(root->place[i][2]));
//source
printf("输入目标数码\n");
for(i=0;i<N;i++)

scanf("%d %d %d",&(target->place[i][0]),&(target->place[i][1]),&(target->place[i][2]));
//answer
printf("解决方案\n");
ans=SolveTree(root,target);
if(ans!=NULL)

head=CreBit();
head->mum=ans;
invert(head);
Printf(head);

else
printf("无解\n");

void move(bitree*p,int k)

int i,j,inter,I,J;
for(I=0;I<N;I++)
for(J=0;J<N;J++)
if(p->place[I][J]==0)i=I;j=J;
switch (k)

case 1:inter=p->place[i][j];p->place[i][j]=p->place[i][j-1];p->place[i][j-1]=inter;break;//左移空格
case 2:inter=p->place[i][j];p->place[i][j]=p->place[i][j+1];p->place[i][j+1]=inter;break;//右移空格
case 3:inter=p->place[i][j];p->place[i][j]=p->place[i-1][j];p->place[i-1][j]=inter;break;//上移空格
case 4:inter=p->place[i][j];p->place[i][j]=p->place[i+1][j];p->place[i+1][j]=inter;break;//下移空格


bitree* CreBit()

bitree* p=new bitree;
p->mum=NULL;
p->next=NULL;
p->son0=NULL;
p->son1=NULL;
p->son2=NULL;
p->son3=NULL;
return p;

int Judge(bitree*a,bitree*b)

int i,j,k=1; //相等矩阵
if(a==NULL||b==NULL)
return 0;
else

for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(a->place[i][j]!=b->place[i][j])k=0;//不等矩阵
return k;


void invert(bitree* head)

bitree*r,*p,*q;
r=head->mum;
p=head;
while(p->mum!=NULL)
p=p->mum;
q=p;
while(r!=p)

while(r->mum!=p)
r=r->mum;
p->mum=r;
p=r;
r=head->mum;

p->mum=NULL;
head->mum=q;

void Printf(bitree*head)

bitree*p;
int i,j;
p=head->mum;
while(p!=NULL)

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

printf("\n");
for(j=0;j<N;j++)
printf("%d ",p->place[i][j]);

printf("\n\n");
p=p->mum;


LinkQueue* InitQueue()

LinkQueue* q=new LinkQueue;
q->front=q->rear=new bitree;
q->front->next=NULL;
return q;

void EnterQueue(LinkQueue* q,bitree* x)

q->rear->next=x;
q->rear=x;
x->next=NULL;

bitree* Dequeue(LinkQueue*q)

bitree*s;
if(q->front==q->rear) return NULL;
else

s=q->front->next;
if(s->next==NULL)

q->front->next=NULL;
q->rear=q->front;
return s;

else

q->front->next=s->next;
return s;



void Assign(bitree* a,bitree* b)

int i,j;
for(i=0;i<N;i++)
for(j=0;j<N;j++)

a->place[i][j]=b->place[i][j];


bitree* SolveTree(bitree* root,bitree* target)

int i,j,I,J;
bitree* tp,*ap;
LinkQueue* q=InitQueue();//open
LinkQueue* p=InitQueue();//close
EnterQueue(q,root);
if(AnsJud(root,target))

while(1)

ap=Dequeue(q);
EnterQueue(p,ap);
for(I=0;I<N;I++)

for(J=0;J<N;J++)
if(ap->place[I][J]==0)i=I;j=J;//Find out '0' ,Position i,j

if(Judge(ap,target))
return ap;
else

if(i>0)//上移

tp=CreBit();
Assign(tp,ap);
move(tp,3);
if(Search(p,tp))

free(tp);

else

ap->son0=tp;tp->mum=ap;
EnterQueue(q,tp);



if(i<N-1)//下移

tp=CreBit();
Assign(tp,ap);
move(tp,4);
if(Search(p,tp))

free(tp);

else

ap->son1=tp;tp->mum=ap;
EnterQueue(q,tp);


if(j>0)//左移

tp=CreBit();
Assign(tp,ap);
move(tp,1);
if(Search(p,tp))

free(tp);

else

ap->son2=tp;tp->mum=ap;
EnterQueue(q,tp);


if(j<N-1)//右移

tp=CreBit();
Assign(tp,ap);
move(tp,2);
if(Search(p,tp))

free(tp);

else

ap->son3=tp;tp->mum=ap;
EnterQueue(q,tp);





else
return NULL;

int Search(LinkQueue* q,bitree* tp)

bitree* a;
if(q->front==q->rear||tp==NULL)
return 0;
else

a=q->front->next;
while((a!=NULL)&&!Judge(a,tp))

a=a->next;

if(Judge(a,tp))return 1;
else return 0;


int AnsJud(bitree* root,bitree* target)

int b[N*N],c[N*N];
int i,j,a;
a=0;
for(i=0;i<N;i++)

for(j=0;j<N;j++)
b[j+a]=root->place[i][j];
a=a+N;

a=0;
for(i=0;i<N;i++)

for(j=0;j<N;j++)
c[j+a]=target->place[i][j];
a=a+N;

if((Unorder(b)%2)!=(Unorder(c)%2))
return 0;
else
return 1;

int Unorder(int *a)

int i,j,temp;
temp=0;
for(i=0;i<N*N;i++)
if(a[i]==0)j=i;
for(i=j;i<(N*N-1);i++)

a[i]=a[i+1];

a[N*N-1]=0;
for(i=N*N-2;i>0;i--)

for(j=0;j<i;j++)
if(a[j]>a[i])temp++;

return temp;

直接黏贴就能用了,如果想做15数码的问题改一下输入和输出就好了广度优先
参考技术B 三种算法在一定条件下均可得到八数码问题的解。但是广度优先算法当目标的深度较深时,产生很多冗余节点,空间消耗很大(在运行中曾造成过死机)。有限深度优先算法在时间或空间复杂度上均没有明显的优势,但如果目标深度较深而且路径选择得当的话,可以较快地得到解答。A*算法可以消耗较少的空间解决问题,但是由于每次选择均需要寻找估价函数最小的节点,因此当深度增加相应的节点数目增加时,A*算法在时间上并不占优势。然而,A*算法总可以在有限的时间内得到问题的解。

八数码的A*与IDA*算法-搜索进阶练习1

八数码的A*与IDA*算法-搜索进阶练习1


hdu1043:http://acm.hdu.edu.cn/showproblem.php?pid=1043
poj1077:http://poj.org/problem?id=1077
题意:众所周知的八数码问题,就不再描述了。不得不说,为了练习A*以及IDA*就直接看题解了。 了解之后分析如下。
解题报告:
先谈A*。A*是一种 启发式搜索,也就是说在每次搜索前,先进行在状态空间中对每一个搜索的位置通过f(n) = g(n) + h(n)进行评估,得到最好的位置,再从这个位置,进行搜索直到目标。其中f(n)是从初始点经由节点n到目标点的估价函数,g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。
然后是IDA*。A*算法和迭代加深算法的结合。
1.搜索框架? A*(IDA*)算法。搜索直到终态结束,或该状态非法进行下一状态。 2.状态? 直接记录每个状态的maze、x位置信息以及A*所需f、g、h。同时使用hash值指向所有状态。 3.状态转移? x与上下左右互换,更新状态信息。 4.剪枝? 一个简单有效的剪枝逆序数为奇则必定无解。 5.hash以及h? hash可通过各位置逆序数乘位置散列值之和散列。h为所有数字块的曼哈顿距离和。
具体而言。A*通过构造优先队列实现搜索。构造优先队列时当f相同时按照g值从大到小排序,这样又是一个很给力的减枝。
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;

//A*: 因为每次移动都会影响一个点的曼哈顿距离(不算x)
//构造h()为所有数字块的曼哈顿距离和,用逆序数hash(算x)
//根据逆序数奇偶性(不算x)减掉无法到达的情况,
struct Node

	char maze[3][3];
	int r, c, f, g, h;		//(x, y); f = g + h
							/*f(n) 是从初始点经由节点n到目标点的估价函数,
							g(n) 是在状态空间中从初始节点到n节点的实际代价,
							h(n) 是从n到目标节点最佳路径的估计代价。*/
	Node() 
	Node(const Node & rhs)
	
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				maze[i][j] = rhs.maze[i][j];
		r = rhs.r, c = rhs.c, g = rhs.g, f = rhs.f, h = rhs.h;
	
	//估价函数小的先出队,相同则实际代价大的先出队
	friend bool operator < (const Node & a, const Node & b)
	
		if (a.f == b.f) return a.g < b.g;
		return a.f > b.f;
	
st;

const int dir[4][2] =   1,0 , 0,1 , -1,0 , 0,-1  ;//方向
char dr[5] = "drul";										//方向查找表
int pos[9][2] = 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2;		//位置信息		
int fac[9] =  1, 1, 2, 6, 24, 120, 720, 5040, 40320 ;		//hash散列因子
char path[370000];
bool vis[370000];
int pre[370000];
int shash;

int h(const Node s)			//估价函数,偏移步数

	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			if (s.maze[i][j] == 'x') continue;
			int c = s.maze[i][j] - '1';
			int x = pos[c][0], y = pos[c][1];
			ans += (abs(x - i) + abs(y - j));
		
	return ans;


int hash_node(const Node s) //hash散列,储存状态

	int ans = 0;
	char str[10];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			int cnt = 0;
			str[i * 3 + j] = s.maze[i][j];
			for (int k = 0; k < i * 3 + j; k++)
				if (str[k] > str[i * 3 + j]) cnt++;
			ans += fac[i * 3 + j] * cnt;
		
	return ans;


bool check_h()				//逆序为奇返回true

	int cnt = 0;
	char str[10];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			str[i * 3 + j] = st.maze[i][j];
			if (st.maze[i][j] == 'x') continue;
			for (int k = 0; k < i * 3 + j; k++)
			
				if (str[k] == 'x') continue;
				if (str[k] > str[i * 3 + j]) cnt++;
			
		
	return cnt & 1;


bool bfs()

	priority_queue<Node> q;
	q.push(st);
	memset(vis, false, sizeof(vis));
	vis[shash] = true;
	while (!q.empty())
	
		Node u = q.top(); q.pop();
		int ihu = hash_node(u);
		for (int d = 0; d < 4; d++)
		
			Node v(u);
			v.r = u.r + dir[d][0], v.c = u.c + dir[d][1];
			if (v.r < 0 || v.r >= 3 || v.c < 0 || v.c >= 3) continue;
			//更新地图
			v.maze[u.r][u.c] = v.maze[v.r][v.c];
			v.maze[v.r][v.c] = 'x';					
			v.g++, v.h = h(v); v.f = v.g + v.h;	//更新f = g + h
			int ihv = hash_node(v);
			if (vis[ihv]) continue;
			vis[ihv] = true; pre[ihv] = ihu;
			path[ihv] = dr[d];
			if (ihv == 0) return true;			//搜索结束
			q.push(v);
		
	
	return false;


void print(int h)

	if (h != shash)
	
		print(pre[h]);
		putchar(path[h]);
	


char in[100];

int main()

	while (gets_s(in))
	
		for (int i = 0, x = 0, y = 0; in[i]; i++)
			if ((in[i] <= '8' && in[i] >= '1') || in[i] == 'x')
			
				st.maze[x][y] = in[i];
				if (in[i] == 'x') st.r = x, st.c = y;
				y++;
				if (y == 3) x++, y = 0;
			
		if (check_h())  puts("unsolvable"); continue; 
		st.g = 0; st.h = h(st); st.f = st.h;
		if ((shash = hash_node(st)) == 0)  puts(""); continue; 
		int thash = bfs();
		print(0);			//递归输出答案
		puts("");
	
	return 0;


而IDA*的话,和A*相同的h()函数。当前h深度下一层一层地进行搜索
#include <cstdio>
#include <algorithm>
using namespace std;

struct Node

	int r, c, f, g, h;
	char maze[3][3];
	Node()
	Node(const Node & rhs) 
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				maze[i][j] = rhs.maze[i][j];
		r = rhs.r, c = rhs.c;
	
ts, s;

const int maxn = 370000;
const int dir[4][2] =   1,0 , 0,1 , -1,0 , 0,-1  ;
const char dr[5] = "drul";
const int fac[9] =  1,1,2,6,24,120,720,5040,40320 ;
const int pos[][2] =  0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2 ;
char path[maxn];
bool vis[maxn];
int deep;

int h(const Node st) 
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			if (st.maze[i][j] == 'x') continue;
			int c = st.maze[i][j] - '1';
			ans += abs(pos[c][0] - i) + abs(pos[c][1] - j);
		
	return ans;


int mhash(const Node st) 
	char str[10];
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			str[i * 3 + j] = st.maze[i][j];
			int cnt = 0;
			for (int k = 0; k < 3 * i + j; k++)
				if (str[k] > str[i * 3 + j]) cnt++;
			ans += cnt * fac[i * 3 + j];
		
	return ans;


bool check() 				//剪枝, 逆序数为奇返回1
	char str[10];
	int cnt = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			str[i * 3 + j] = s.maze[i][j];
			if (s.maze[i][j] == 'x') continue;
			for (int k = 0; k < 3 * i + j; k++)
			
				if (str[k] == 'x') continue;
				if (str[k] > str[i * 3 + j]) cnt++;
			
		
	return cnt & 1;


int dfs(int d) 
	//搜索完成
	if (h(ts) == 0) return true;
	//超过当前的搜索深度   
	if (h(ts) + d > deep) return false;
	//当前r、c
	int r = ts.r, c = ts.c;
	for (int dd = 0; dd < 4; dd++)
	
		int nr = ts.r + dir[dd][0], nc = ts.c + dir[dd][1];
		if (nr < 0 || nc < 0 || nr > 2 || nc > 2) continue;
		ts.maze[r][c] = ts.maze[nr][nc];
		ts.maze[nr][nc] = 'x';
		int ihv = mhash(ts);
		if (vis[ihv]) 
			ts.maze[nr][nc] = ts.maze[r][c];
			ts.maze[r][c] = 'x';
			continue;
		
		vis[ihv] = true;
		ts.r = nr, ts.c = nc;
		path[d] = dr[dd];
		if (dfs(d + 1)) return true;
		vis[ihv] = false;
		ts.r = r, ts.c = c;
		ts.maze[nr][nc] = ts.maze[r][c];
		ts.maze[r][c] = 'x';
	
	return false;


int main() 
	char in[100];
	while (gets_s(in))
	
		for (int i = 0, x = 0, y = 0; in[i]; i++)
			if (in[i] == 'x' || (in[i] > '0' && in[i] < '9'))
			
				if (in[i] == 'x') s.c = y, s.r = x;
				s.maze[x][y++] = in[i];
				if (y == 3) y = 0, x++;
			
		if (check())  puts("unsolvable"); continue; 
		memset(vis, false, sizeof(vis));
		vis[mhash(s)] = true;
		ts = s; deep = 0;
		while (true)
		
			if (dfs(0)) break;
			deep++;
		
		for (int i = 0; i < deep; i++)
			putchar(path[i]);
		puts("");	
	
	return 0;

最后打卡一下。之前Ubuntu的ibus默认的中文输入法双拼的,当时就感觉是一堆乱码,玩毛。不几天前,尝试用双拼了。

以上是关于八数码问题算法,谁有?的主要内容,如果未能解决你的问题,请参考以下文章

八数码问题的问题,有解条件以及求解算法(宽度优先搜索)

人工智能基于八数码问题对比搜索算法的性能

人工智能基于八数码问题对比搜索算法的性能

怎么样判断一个八数码问题有解还是无解啊?

A*算法求解八数码难题(python实现)

八数码问题解析