最近人工智能学的一塌糊涂,上一节课得头疼好几天,太菜了。
这里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;
}