康托展开:
公式:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!;
利用这个公式,我们可以求出在一列数中,已知的这一列数是这列数的全排列的字典序中的的几个。我们可以应用康拓展开来求出一列数的全排列,也可以用康托展开进行判重。
int KT(int n,int s[])
{
int i,j,sum=0,t=0;
for(i=0;i<n;++i)
{
t=0;
for(j=i+1;j<n;++j)
if(s[j]<s[i])
t+=1;
sum+=t*fac[n-i-1];
}
return sum+1;
}
双向广搜:
双向广搜:
当已知目标状态和起始状态时,在搜素时可以选择双向广搜,双向广搜,这样在搜索时的时间复杂度就由原来的b^len缩小到2*b^(len/2),同时空间也会缩小,只要在搜索时多开一个队列,当在这个队列中的状态已经在另一个队列中出现时,就可以停止搜索了,但其中有一个需要注意的问题,这也是为什么我的第一个双向广搜做了好久才A的原因:
在搜索时,我们不应该交替节点搜索:
while( )
在搜索时,我们不应该交替节点搜索:
while( )
{
扩展一个正向的节点;
if(达到状态)
{
break;
}
扩展一个反向的节点
if(达到状态)
{
break;
}
}
这种搜索方法是有问题的,当我们在逐个扩展时,可能会等不到这一层的最优解被扩展出就停止搜索了
这种搜索方法是有问题的,当我们在逐个扩展时,可能会等不到这一层的最优解被扩展出就停止搜索了
求S-T的最短路,交替节点搜索(一次正向节点,一次反向节点)时
Step 1 : S –> 1 , 2
Step 2 : T –> 3 , 4
Step 3 : 1 –> 5
Step 4 : 3 –> 5 返回最短路为4,错误的,事实是3,S-2-4-T所以当我们在进行搜索时,应该把正向或反向的队列的这一步中的状态全部扩展完以后,再去扩展另一个方向中的队列,这样我们就可以保证不会出现最优的情况还没有被搜索出来的时候就已经break掉了。
例题: codevs 1225
八数码难题
题目描述 Description
题目描述 Description
Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.
问题描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入描述 Input Description
输入初试状态,一行九个数字,空格用0表示
输出描述 Output Description
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
样例输入 Sample Input
283104765
样例输出 Sample Output
4#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct point
{
int b[3][3],xx,yy;
}l[1000000][3];
int fac[10]={1,1,2,6,24,120,720,5040,40320,362880};
int c[1000000][3]={0},h1,h2,t1,t2,xi[4]={-1,0,0,1},yi[4]={0,-1,1,0},en[9]={1,2,3,8,0,4,7,6,5};
char a[10];
bool f[500000][3],ff=false;
int KT(int s[])
{
int i,j,sum=0,t=0;
for(i=0;i<9;++i)
{
t=0;
for(j=i+1;j<9;++j)
if(s[j]<s[i])
t+=1;
sum+=t*fac[9-i-1];
}
return sum+1;
}
int change(int x,int y)
{
int i,j,aaa[9];
for(i=0;i<3;++i)
for(j=0;j<3;++j)
aaa[i*3+j]=l[x][y].b[i][j];
return KT(aaa);
}
int main()
{
int i,j,xxx,yyy,aa,bb,k,u;
scanf("%s",&a);
memset(f,1,sizeof(f));
for(i=0;i<9;++i)
{
l[1][1].b[i/3][i%3]=a[i]-'0'; l[1][2].b[i/3][i%3]=en[i];
if(a[i]=='0') l[1][1].xx=i/3, l[1][1].yy=i%3;
if(en[i]==0) l[1][2].xx=i/3, l[1][2].yy=i%3;
}
aa=change(1,1); bb=change(1,2);
f[aa][1]=false; f[bb][2]=false; h1=h2=t1=t2=1;
while(h1<=t1&&h2<=t2)
{
for(i=0;i<4;++i)
{
xxx=l[h1][1].xx+xi[i]; yyy=l[h1][1].yy+yi[i];
if(xxx>=0&&xxx<3&&yyy>=0&&yyy<3)
{
for(j=0;j<3;++j)
for(k=0;k<3;++k)
l[t1+1][1].b[j][k]=l[h1][1].b[j][k];
l[t1+1][1].xx=xxx; l[t1+1][1].yy=yyy;
l[t1+1][1].b[xxx][yyy]=0; l[t1+1][1].b[l[h1][1].xx][l[h1][1].yy]=l[h1][1].b[xxx][yyy];
u=change(t1+1,1);
if(f[u][1])
{
t1+=1; c[t1][1]=c[h1][1]+1; f[u][1]=false;
if(!f[u][2])
{
ff=true;
for(int k=1;k<=t2;++k)
if(change(k,2)==u)
{
printf("%d\n",c[t1][1]+c[k][2]);
break;
}
break;
}
}
}
}
if(ff) break;
h1+=1;
for(i=0;i<4;++i)
{
xxx=l[h2][2].xx+xi[i]; yyy=l[h2][2].yy+yi[i];
if(xxx>=0&&xxx<3&&yyy>=0&&yyy<3)
{
for(j=0;j<3;++j)
for(k=0;k<3;++k)
l[t2+1][2].b[j][k]=l[h2][2].b[j][k];
l[t2+1][2].xx=xxx; l[t2+1][2].yy=yyy;
l[t2+1][2].b[xxx][yyy]=0; l[t2+1][2].b[l[h2][2].xx][l[h2][2].yy]=l[h2][2].b[xxx][yyy];
u=change(t2+1,2);
if(f[u][2])
{
t2+=1; c[t2][2]=c[h2][2]+1; f[u][2]=false;
if(!f[u][1])
{
ff=true;
for(int k=1;k<=t1;++k)
if(change(k,1)==u)
{
printf("%d\n",c[t2][2]+c[k][1]);
break;
}
break;
}
}
}
}
if(ff) break;
h2+=1;
}
}
1913

被折叠的 条评论
为什么被折叠?



