ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

八数码难题( A*算法详解,附题目链接 )

2021-10-01 14:33:12  阅读:215  来源: 互联网

标签:node 状态 int 步数 数码 节点 include 链接 详解


经典八数码问题,给一个3*3的方阵,上面写着0~8的数字,

求把方阵变换成另一个方阵所需最小步数

题目链接: 八数码难题 洛谷P1379

考虑暴力搜索,一共有9的9次方种状态,超时

A*算法:相比于盲目搜索,每次朝着离目标状态最接近的方向搜索

定义估价函数 f(n)= h(n)+ g(n)

我们要搜索最小步数,因此我们希望 f(n)最小,h(n)是启发函数,是人为构造的,表示当前状态距离目标状态预期需要的最小步数,而 g(n)表示从初始状态到目前走了多少步数,h(n)经过思考后,在本题中可以定义为每个不同数码在当前状态距离目标状态曼哈顿距离的和。

在具体实现A*时,我们按照一般bfs的思路,把起始状态入队,循环取出队列中f(n)最小的状态(这里可以用优先队列优化),然后把该状态可能的后续状态都入队,重复上述步骤,直到取出的状态为最终状态。

不会bfs的同学戳这里:

bfs与dfs详解

不会优先队列(堆)的同学戳这里:

优先队列详解

具体实现如下(加了可视化操作,比原题略有改动)

#include<iostream>
#include<queue>
#include<utility>
#include<map>
#include<cmath>
#include<windows.h> 
#define mp(x,y) make_pair(x,y)

using namespace std;

//输入两个长度为9的数字串,分别代表初始状态和目标状态

typedef struct node{//定义节点为目前棋盘状态
	pair<int,int> center;
	int g[4][4];
}node;

bool operator == (node x,node y){用于比较节点是否相同
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=3;j++)
		{
			if(x.g[i][j]!=y.g[i][j])return 0;
		}
	}
	return 1;
}

bool operator < (node x,node y){//用于查找节点
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=3;j++)
		{
			if(x.g[i][j]<y.g[i][j])return 1;
			else if(x.g[i][j]>y.g[i][j])return 0;
		}
	}
	return 0;
}

void init_node(node &x){//初始化节点
	for(int i=0;i<=3;i++)
	{
		for(int j=0;j<=3;j++)
		{
			x.g[i][j]=0;
		}
	}
}

char o1[15],o2[15];//输入初始状态与目标状态

node s,e;

priority_queue< pair<int,node> > q;

map<node,int> g; //用于记录状态是否出现过,避免重复搜索

map<node,node> pre; //记录前驱节点

int dx[4]={-1,0,1,0};//搜索的四个方向
int dy[4]={0,1,0,-1};

int cal_h(node x)//计算启发函数(两个状态各个数位曼哈顿距离的和)
{
	int sum=0;
	pair<int,int> h[9];//预处理优化
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=3;j++)
		{
			h[x.g[i][j]]=mp(i,j);
		}
	}
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=3;j++)
		{
			sum+=abs(i-h[e.g[i][j]].first)+abs(j-h[e.g[i][j]].second);
		}
	}
	return sum;
}

void print_mp(node x)//输出当前状态
{
	system("cls");
	for(int i=1;i<=4;i++)cout<<"\n";
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=6;j++)cout<<"\t";
		for(int j=1;j<=3;j++)
		{
			cout<<x.g[i][j]<<"  ";
		}
		cout<<"\n\n";
	}
	Sleep(1000);
}

void print(node x)//递归输出
{
	if(x==s)print_mp(x);
	else{
		print(pre[x]);
		print_mp(x);
	}
}

int main()
{
	cin>>o1+1;
	cin>>o2+1;
	
	init_node(s);//将输入转化为节点状态
	for(int i=1;i<=9;i++)
	{
		s.g[(i+2)/3][(i-1)%3+1]=o1[i]-'0';
		if(s.g[(i+2)/3][(i-1)%3+1]==0)s.center=mp((i+2)/3,(i-1)%3+1);
	}
	
	init_node(e);
	for(int i=1;i<=9;i++)
	{
		e.g[(i+2)/3][(i-1)%3+1]=o2[i]-'0';
		if(e.g[(i+2)/3][(i-1)%3+1]==0)e.center=mp((i+2)/3,(i-1)%3+1);
	}
	
    //广搜,优先队列优化
	g[s]=0;
	
	q.push(mp(-(cal_h(s)+g[s]),s));
	
	while(!q.empty())
	{ 
		node tmp=q.top().second;
		q.pop();
		if(tmp==e)break;
		if(g[tmp]==25)//如果搜索的次数过多,结束搜索
		{
			cout<<"-1"<<endl;
			return 0;
		}
		int x=tmp.center.first;
		int y=tmp.center.second;
		for(int i=0;i<=3;i++)
		{
			int xx=x+dx[i];
			int yy=y+dy[i];
			if(xx>=1&&xx<=3&&yy>=1&&yy<=3)
			{
				node u=tmp;
				swap(u.g[x][y],u.g[xx][yy]);
				if(g.find(u)==g.end())//如果没搜索过该状态,将其入队
				{
					u.center=mp(xx,yy);
					g[u]=g[tmp]+1;
					pre[u]=tmp;
					q.push(mp(-(cal_h(u)+g[u]),u));
				}
			}
		}
	}
	
	print(e);
	
	return 0;
}



A*练习题:

骑士精神 洛谷P2324  A*经典题目

k短路 洛谷P4467  比较有难度的题目,需要结合其他算法

                                                                                                                                  转载请注明出处

标签:node,状态,int,步数,数码,节点,include,链接,详解
来源: https://blog.csdn.net/masterhero666/article/details/120577358

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有