八数码问题 A*算法与广搜实现

博主在学习人工智能的A*算法时遇到难题。一是不知如何完整存储图,最初想用邻接表但有双向边修改问题,最终只在节点数组存信息;二是用C++的heap找最小值花费大,也考虑过优先队列思路但未验证,最后给出广搜代码和A*算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近人工智能学的一塌糊涂,上一节课得头疼好几天,太菜了。

这里A*书上讲的是通用图搜索算法的扩展,但是这个图怎么完整存下来我不会,一开始的想法是开一个邻接表u v fisrt next

然后每次生成新的节点时加入边,但这个边是单向的话无法表示指向父亲的指针,如果是双向的话,又难以修改(邻接表如何删除和修改边,这个有待学习),最后我只在节点数组中存储了有关信息,该点的状态(及数字) 花费(该状态的估值) 层级 和父指针(用数字表示)

这样整个的图并没有存储,这是第一个问题

二是我是用了C++的heap来寻找最小值,每次填入节点都要make_heap和sort_heap,想必花费不小,但这样可以修改对应节点的层级和父指针,在优先队列中就不能做到(其实这里还有一种思路是使用优先队列,每次发现要生成的节点已经在OPEN中时,不用去修改优先队列中的点,只要push_back一个新节点进去,花费为当前层级,父指针指向生成他的节点,假设这两个节点都有可能出队列,只要一个花费小的先出队列,那么另一个即使有机会出队列,他可以变化的所有状态也已经被遍历过,所以这个节点就不会造成影响,这个想法我并没有写出来验证

 下面给出广搜代码

#include<bits/stdc++.h>
using namespace std;
int book[400000];
int num[400000];
char tep[11]="012345678";
int que[400000][4];//0记录数字,1记录0的位置,2记录当前走了多少步,3记录当前界定啊上一步节点的编号 
int nums,flag,ans;
int aim=123804765;
int now=123456780;
int dir[9][4]={{-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},
					{0,-1,6,4},{1,3,7,5},{2,4,8,-1},
					{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1}};//代表在0-8的位置应该与谁交换,按逆时针方向排列 
void perm(int st,int len)
{
	if(st==len-1)
	{
		int tp;
		sscanf(tep,"%09d",&tp);
		num[++nums]=tp;
	}
	else
	{
		for(int i=st;i<len;i++)
		{
			swap(tep[i],tep[st]);
			perm(st+1,len);
			swap(tep[i],tep[st]);
		}	
	}	
}
int bin_sea(int n,int l,int r)
{
	int mid;
	while(l<r)
	{
		mid=(l+r)/2;
		if(n>num[mid])
			l=mid+1;
		else 
			r=mid-1;
	}
	return l;
}
int query(int n)
{
	char tp[10];
	sprintf(tp,"%09d",n);
	for(int i=0;i<9;i++)
		if(tp[i]=='0')
			return i;
}
void bfs(int now,int pos)
{
	int head=0,tail=1;
	book[bin_sea(now,1,nums)]=1;
	que[head][0]=now,que[head][1]=pos,que[head][2]=0,que[head][3]=-1;
	while(head!=tail && flag==0)
	{
		char cur[10];
		int p=que[head][1];
		sprintf(cur,"%09d",que[head][0]);
		for(int i=0;i<=3&&!flag;i++)
		{
			int d=dir[p][i];
			if(d!=-1)
			{
				swap(cur[p],cur[d]);
				int tp;
				sscanf(cur,"%09d",&tp);
				if(book[bin_sea(tp,1,nums)]==0)
				{
					que[tail][0]=tp;
					que[tail][1]=d;
					que[tail][2]=que[head][2]+1;
					que[tail][3]=head;
					tail++;
					book[bin_sea(tp,1,nums)]=1;
					if(tp==aim)
					{
						flag=1;
						ans=tail-1;
					}
				}
				swap(cur[p],cur[d]);
			}
		}
		head++;
	}
	cout<<tail<<endl;
}
void print(int n)
{
	char str[10];
	sprintf(str,"%09d",n);
	for(int i=0;i<9;i++)
		printf("%c%s",str[i],(i+1)%3==0?"\n":" ");
}
int out(int n)
{
	int cal=0;
	n=que[n][3];
	while(n!=-1)
	{
		print(que[n][0]);
		printf("%d\n",que[n][3]);
		n=que[n][3]; 
		cal++;
	}
	cout<<"Total:"<<cal<<endl;
}
int main()
{
	perm(0,9);
	sort(num+1,num+nums+1);
	bfs(now,query(now));
	out(ans);
	return 0;
}

下面是A*算法

#include<bits/stdc++.h>
using namespace std;
int book_open[400000],book_closed[400000];
int num[400000];
char tep[11]="012345678";
int nums,flag,ans;
int aim=123804765;
int now=283164705;//初始状态 
int corr_dir[10]={4,0,1,2,5,8,7,6,3};//每个数字应在的位置 
int dir[9][4]={{-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},
					{0,-1,6,4},{1,3,7,5},{2,4,8,-1},
					{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1}};//代表在0-8的位置应该与谁交换,按逆时针方向排列 
struct node{
	int level,cost,pre,status,pos;
	void setcost_1()//不在位置的点数 
	{
		int c=0;
		char str[10];
		sprintf(str,"%09d",status);
		for(int i=1;i<9;i++)
		{
			if(str[corr_dir[i]]-'0'!=i)
				c++;
		}
		cost=c;
	}
	void setcost_2()//不在位置的点到应在位置的绝对值之和 
	{
		int t=0;
		char str[10];
		sprintf(str,"%09d",status);
		for(int i=1;i<9;i++)
		{
			if(str[corr_dir[i]]-'0'!=i)
			{
				for(int j=0;j<=8;j++)
					if(str[j]-'0'==i)
					{
						int a,b,c,d;
						a=j/3+1,b=j%3+1;
						c=corr_dir[i]%3+1,d=corr_dir[i]/3+1;
						t+=fabs(a-c)+fabs(b-d);
					}
			}
		}
		cost=t;
	}
	node(int _status=0,int _pos=0,int _level=0,int _pre=0):status(_status),pos(_pos),level(_level),pre(_pre){
		setcost_1();//这里可以修改估值函数 
	}
	bool operator <(const node &a)const{
		return level+cost>a.level+a.cost;
	}
};
bool cmp(const node &a,const node &b)
{
	return a<b;//此处符号不确定,再修改 
}
vector<node> open;
node N[400000];
void perm(int st,int len)
{
	if(st==len-1)
	{
		int tempnum;
		sscanf(tep,"%09d",&tempnum);
		num[++nums]=tempnum;
	}
	else
	{
		for(int i=st;i<len;i++)
		{
			swap(tep[i],tep[st]);
			perm(st+1,len);
			swap(tep[i],tep[st]);
		}
	}
}
int bin_sea(int n)
{
	int l=1,r=nums,mid;
	while(l<r)
	{
		mid=(l+r)/2;
		if(num[mid]<n)
			l=mid+1;
		else 
			r=mid;
	}
	return l;
}
int find_zero(int n)
{
	char str[10];
	sprintf(str,"%09d",n);
	for(int i=0;i<9;i++)
		if(str[i]=='0')
			return i;
}
void init()
{
	perm(0,9);
	sort(num+1,num+nums+1);
}
void print(int n)
{
	char str[10];
	sprintf(str,"%09d",num[n]);
	for(int i=0;i<9;i++)
		printf("%c%s",str[i],(i+1)%3==0?"\n":" ");
}
void A_star_sea(int st,int pos)
{
	char str[10];
	node a(st,pos,0,-1);
	open.push_back(a);
	book_open[bin_sea(st)]=1;
	N[bin_sea(st)]=a;
	while(open.size() && !flag)
	{
		//cout<<"in the while\n";
		node tempnode=open.front();
		//print(bin_sea(tempnode.status));
		//cout<<tempnode.level<<"   "<<tempnode.cost;
		//cout<<endl;
		pop_heap(open.begin(),open.end(),cmp); 
		open.pop_back();
		book_closed[bin_sea(tempnode.status)]=1;
		if(tempnode.status==aim)
		{
			N[bin_sea(aim)]=tempnode;
			flag=1;
			break;
		}
		sprintf(str,"%09d",tempnode.status);
		int p=tempnode.pos,d;
		for(int i=0;i<=3;i++)
		{
			d=dir[p][i];
			if(d!=-1)
			{
				swap(str[p],str[d]);
				int tempnum;
				sscanf(str,"%09d",&tempnum);
				if(!book_open[bin_sea(tempnum)])//没有出现过 
				{
					node a(tempnum,d,tempnode.level+1,bin_sea(tempnode.status));
					open.push_back(a);
					N[bin_sea(tempnum)]=a;
					book_open[bin_sea(tempnum)]=1;
					make_heap(open.begin(),open.end(),cmp);
					sort_heap(open.begin(),open.end(),cmp);
				}
				else if(book_open[bin_sea(tempnum)])//在OPEN表中,可能要修改他的前向指针 
				{                                    //这里在节点数组和表示OPEN表的堆中都要修改指针和层级 
					for(vector<node>::iterator j=open.begin();j<open.end();j++)
					{
						if((*j).status==tempnum)
						{
							if((*j).level>tempnode.level+1)//如果之前的层级比当前生成他的点的层级加一还大,则修改 
							{
								(*j).level=tempnode.level+1;
								(*j).pre=bin_sea(tempnum);
								N[bin_sea(tempnum)].level=tempnode.level+1;;
								N[bin_sea(tempnum)].pre=bin_sea(tempnum);
								make_heap(open.begin(),open.end(),cmp);
								sort_heap(open.begin(),open.end(),cmp);
							}
							break;
						}
					}
				}
				else if(book_closed[bin_sea(tempnum)])//在closed中
				{
					continue ;
				}
				swap(str[p],str[d]);
			}
		}
	}
}

void out()
{
	int cal=0;
	int ed=bin_sea(aim);
	if(flag)
	{
		print(ed);
		ed=N[ed].pre;
		while(ed!=-1)
		{
			cout<<"step: ";
			cout<<++cal<<endl;
			print(ed);
			cout<<endl;
			ed=N[ed].pre;
		}
	}
	else
		cout<<"No answer\n";
}
int main()
{
	init();
	A_star_sea(now,find_zero(now));
	out();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值