您的位置:首页 > 其它

POJ1077(经典的八数码问题)

2012-08-05 11:00 441 查看
很经典的八数码问题,可以用单向广度优先搜索、双向广度优先搜索、A*算法、IDA算法解。

用了双向广度优先搜索和A*算法解,在用A*算法时,纠结了好几天,后来在网上看了一份博客才发现自己错在哪。之后解出来了。虽然做这题时很纠结,不过收获真的很大,痛而快乐着…………

下面贴出用双向广度优先搜索和A*算法做的代码

//A*算法

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=362882;
int n1,n2;                        //n1记录正向搜索的步骤数,n2记录逆向搜索的步骤数
int num[10];
int visit1[maxn],visit2[maxn];    //正向搜索的访问数组,逆向搜索的访问数组
int dir[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
char dir1[4]={'d','u','l','r'};
char dir2[4]={'u','d','r','l'};

struct node                        //棋盘状态结点
{
int x;
int y;
int step;                     //到达该状态走了多少步
char map[3][3];
}s,e;

struct path                        //路径
{
int dir;                       //到该步的方向
int pre;                       //该步的前一步
}path1[maxn],path2[maxn];          //正向搜索路径,逆向搜索路径

void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}

bool check(int x,int y)
{
if(x>=0 && x<3 && y>=0 &&y<3)
return true;
else
return false;
}

void Init()
{
int i,j;
char ch;
n1=0;
n2=0;
num[0]=1;
for(i=1;i<=9;i++)
num[i]=num[i-1]*i;
e.x=2;                         //最终状态的坐标值
e.y=2;
for(i=0;i<3;i++)               //初始化棋盘上的各个格子的值
for(j=0;j<3;j++)
e.map[i][j]=i*3+j+1+'0';
for(i=0;i<3;i++)
for(j=0;j<3;j++)
{
cin>>ch;
if(ch=='x')
{
s.map[i][j]='9';
s.x=i;
s.y=j;
}
else
s.map[i][j]=ch;
}
}

int calculate(char m[][3])     //计算所处的状态
{
int i,j,a,b,v,count,temp;
v=0;
for(i=0;i<9;i++)
{
count=0;
a=m[i/3][i%3]-48;
for(j=i+1;j<9;j++)
{
b=m[j/3][j%3]-48;
if(a>b)
count++;
}
v+=count*num[a-1];
}
return v;
}

bool TBFS()
{
int i,x,y,th,temp;
queue<node> L,R;
node point;
s.step=0;
e.step=0;
L.push(s);
R.push(e);
visit1[calculate(s.map)]=1;
visit2[calculate(e.map)]=1;
while(!L.empty() || !R.empty())
{
if(!L.empty())
{
point=L.front();
L.pop();
temp=point.step;          //暂时存储该状态是第几状态,及所用的步骤数
for(i=0;i<4;i++)
{
x=point.x+dir[i][0];
y=point.y+dir[i][1];
if(check(x,y))
{
swap(point.map[x][y],point.map[point.x][point.y]);
swap(x,point.x);
swap(y,point.y);
th=calculate(point.map);
if(visit2)         //逆向搜索已经访问了该状态,访问交叉,结束搜索
{                      //搜索中的每一步都对应棋盘的一个状态
path1[++n1].pre=temp; //正向搜索路径上到达该步懂得前一步即为temp
path1[n1].dir=i;
n2=visit2-1; //逆向搜索的总步数减少一,因为正向搜索的步骤数加了一
return true;
}
if(!visit1)
{
point.step=++n1;
path1[n1].pre=temp;
path1[n1].dir=i;
visit1=n1+1;   //编号为th的状态是正向搜索第n1+1步访问的,状态编号从一开始,即厨师和最终的状态都记一,步骤数从0开始
L.push(point);
}
swap(x,point.x);
swap(y,point.y);
swap(point.map[x][y],point.map[point.x][point.y]);
}
}
}
if(!R.empty())
{
point=R.front();
R.pop();
temp=point.step;
for(i=0;i<4;i++)
{
x=point.x+dir[i][0];
y=point.y+dir[i][1];
if(check(x,y))
{
swap(point.map[x][y],point.map[point.x][point.y]);
swap(x,point.x);
swap(y,point.y);
th=calculate(point.map);
if(visit1)     //正向已经搜索了该状态,搜索交叉,搜索结束
{
path2[++n2].pre=temp;
path2[n2].dir=i;
n1=visit1-1;
return true;
}
if(!visit2)
{
point.step=++n2;
path2[n2].pre=temp;
path2[n2].dir=i;
visit2=n2+1;
R.push(point);
}
swap(x,point.x);
swap(y,point.y);
swap(point.map[x][y],point.map[point.x][point.y]);
}
}
}
}
return false;
}

int main()
{
int i,k;
int d[maxn];
Init();
if(calculate(s.map)==calculate(e.map));
else if(TBFS())
{
k=0;
while(n1>0)
{
d[k++]=path1[n1].dir;
n1=path1[n1].pre;
}
for(i=k-1;i>=0;i--)
printf("%c",dir1[d[i]]);
while(n2>0)
{
printf("%c",dir2[path2[n2].dir]);
n2=path2[n2].pre;
}
printf("\n");
}
else
printf("unsolvable\n");
return 0;
}


//双向广度优先搜索

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=362882;
int st,end,pre[maxn],a[10];
char path[maxn];              //路径
bool visit[maxn];             //标志访问数组
int frac[9]={1,1,2,6,24,120,720,5040,40320};

struct node
{
int map[10];
int state;
int x;
int g;
int f;
bool operator <(const node&a)const
{
return f>a.f;                    //在优先队列中,f较小的优先出队
}
};

int hash(int s[])                         //计算所处的状态
{
int i,j,v,count;
v=0;
for(i=1;i<=9;i++)
{
count=0;
for(j=1;j<i;j++)
if(s[j]>s[i])
count++;
v+=count*frac[i-1];
}
return v;
}

int geth(int s[])       //计算启发函数值,即曼哈顿距离
{
int i,a,b,c,d,v;
v=0;
for(i=1;i<=9;i++)
{
if(s[i]==9)
continue;
a=(i+2)/3;     //原来位置的x
b=(i-1)%3+1;   //原来位置的y
c=(s[i]+2)/3;
d=(s[i]-1)%3+1;
v+=abs(a-c)+abs(b-d);
}
return v;
}

void Astar()
{
int i,th;
priority_queue<node> q;
node point1,point2;
point1.state=hash(a);
st=point1.state;
visit[st]=true;
if(st==end)
return ;
point1.g=0;           //搜索的深度
point1.f=geth(a);
for(i=1;i<=9;i++)
{
point1.map[i]=a[i];
if(a[i]==9)
point1.x=i;
}
q.push(point1);
while(!q.empty())
{
point1=q.top();
q.pop();
if((point1.x+2)/3!=1)      //向上交换数字
{
point2=point1;
point2.x=point1.x-3;
point2.map[point1.x]=point2.map[point2.x];
point2.map[point2.x]=9;
th=hash(point2.map);
if(!visit)
{
point2.g++;
point2.f=point2.g+geth(point2.map);
point2.state=th;
pre=point1.state;
path='u';
visit=true;
if(th==end)
return ;
q.push(point2);
}
}
if((point1.x+2)/3!=3)       //向下交换
{
point2=point1;
point2.x=point1.x+3;
point2.map[point1.x]=point2.map[point2.x];
point2.map[point2.x]=9;
th=hash(point2.map);
if(!visit)
{
point2.g++;
point2.f=point2.g+geth(point2.map);
point2.state=th;
pre=point1.state;
path='d';
visit=true;
if(th==end)
return ;
q.push(point2);
}
}
if(point1.x%3!=1)       //向左交换数字
{
point2=point1;
point2.x=point1.x-1;
point2.map[point1.x]=point2.map[point2.x];
point2.map[point2.x]=9;
th=hash(point2.map);
if(!visit)
{
point2.g++;
point2.f=point2.g+geth(point2.map);
point2.state=th;
pre=point1.state;
path='l';
visit=true;
if(th==end)
return ;
q.push(point2);
}
}
if(point1.x%3!=0)       //向右交换数字
{
point2=point1;
point2.x=point1.x+1;
point2.map[point1.x]=point2.map[point2.x];
point2.map[point2.x]=9;
th=hash(point2.map);
if(!visit)
{
point2.g++;
point2.f=point2.g+geth(point2.map);
point2.state=th;
pre=point1.state;
path='r';
visit=true;
if(th==end)
return ;
q.push(point2);
}
}
}
}

int main()
{
int i,j,k;
char str[50];
gets(str);
k=0;
for(i=0;str[i];i++)
{
if(str[i]=='x')
a[++k]=9;
else if(str[i]!=' ')
a[++k]=str[i]-'0';
}
end=0;
memset(visit,0,sizeof(visit));
Astar();
j=0;
while(st!=end)
{
str[j++]=path[end];
end=pre[end];
}
for(i=j-1;i>=0;i--)
printf("%c",str[i]);
printf("\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: