if(a[i]>a[n])m++;
returnm;
}
intcheck(chara[])
{
inti,tem=0;
for(i=0;i<9;i++)//找到空格所在位置
if(a[i]=='9')break;
while(i<6)
{
s[i],&a[i+3]);
i=i+3;
}
while(i<8)
{
s[i],&a[i+1]);
i=i+1;
}//将空格置于右下角的位置来推算是否成立。
数学原理如下:
/*
假设图中的a是3*3数字拼图标准的结果,则对于图b的状态是不可能变换成a的。
证明起来需要用到高等代数里逆序数的概念,
具体的说是用到了一个简单的定理。
定义:
在一个1,2,...,n的排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。
一个排列中逆序的总数就称为这个排列的逆序数。
逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。
如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。
——这是北大《高等代数》上的定义。
定理:
交换一个排列中的两个数,则排列的奇偶性发生改变。
(证明见任何一本《高等代数》)
我们将空格看成数字9(数字9对应空位),按正常顺序看a图,9个数字排列是123456789,其逆序数是0,是偶排列;
b图是123456879,逆序数是1,是奇排列。
我们知道,我们能够移动空块相邻的块,
这里的移动相当于一种特殊的对换(相邻对换),例如:
对于b图,移动6就相当于9和6互换(9向上移动了),
移动7就相当于9和7互换(9向左移动了)。
现在假设从b图经过一系列的平移变到了a图,
则空格块9必然移动(对换)了偶数次(向左一次必然要再向右一次回来,向上一次必然要向下再回来,最终才能够回到右下角的位置),
根据上面的定理最终变成的排列必然是仍然是奇排列(和b相同),
然而a图是偶排列,因而产生矛盾,因此b图不可能通过平移变成最终的a图。
*/
for(i=0;i<9;i++)//求逆置数
{
tem=tem+check1(i,a);
}
returntem%2;
}
voidnextpath(Linklistparent,Linklistchild,intn)
{
child->path=(char*)malloc(strlen(parent->path)+2);
strcpy(child->path,parent->path);
child->path[strlen(parent->path)]=n+48;
child->path[strlen(parent->path)+1]=0;
}
intnext(Linklistparent,Linklist*child,intflag)
{
chartem[10]={0};
inttemp;
sprintf(tem,"%d",parent->data);
*child=(Linklist)malloc(sizeof(LNode));
for(temp=0;temp<9;temp++)//找到空格所在位置
if(tem[temp]=='9')break;
switch(flag)
{
caseU:
s[temp],&tem[temp-3]);nextpath(parent,*child,temp-3);break;
caseD:
s[temp],&tem[temp+3]);nextpath(parent,*child,temp+3);break;
caseL:
s[temp],&tem[temp-1]);nextpath(parent,*child,temp-1);break;
caseR:
s[temp],&tem[temp+1]);nextpath(parent,*child,temp+1);break;
}
(*child)->flag=parent->flag;
sscanf(tem,"%d",&((*child)->data));
(*child)->fangxaing=flag;
return0;
}
intf(intn)
{//哈希函数
intm=0,i,a[8]={40320,5040,720,120,24,6,2,1};
chartem[9];
sprintf(tem,"%d",n);
for(i=0;i<8;i++)
{
m=m+(tem[i]-49+check1(i,tem)-i)*a[i];
}
//a=((n/100000000)-1)*40320+(((n%100000000)/10000000)-1)*5040+(((n%10000000)/1000000)-1)*720+(((n%1000000)/100000)-1)*120;
//m=a+(((n%100000)/10000)-1)*24+(((n%10000)/1000)-1)*6+(((n%1000)/100)-1)*2+(((n%100)/10)-1);
returnm+1;
}
intinhxb(Linklisttem,Linklista[])
{//哈希函数为所有比表示这个状态的各位不相等的九位数小的各位不相等的九位数的个数,所以不会产生冲突
//将tem放入正确的位置,并利用结点中的next构造一个头结点为hxb[0]的单链表便于之后释放空间
intn,m;
n=tem->data;
m=f(n);
a[m]=tem;
tem->next1=a[0];
a[0]=tem;
return1;
}
intbfs(QueueQ,Linklistparent,Linklisthxb[])
{//对结点tem进行宽度优先搜索,并将子状态入队列,
intm,x,y;//x,y表示空格在3*3矩阵中的位置,
chartemp[9];
Linklistchild;
m=f(parent->data);
if(hxb[m]!
=0)
{
if(hxb[m]->flag==parent->flag)
return1;
else
return0;
}
inhxb(parent,hxb);//进入已搜索的列表
sprintf(temp,"%d",parent->data);
for(m=0;m<9;m++)//找到空格所在位置
if(temp[m]=='9')break;
y=m%3+1;x=m/3+1;
if(x<3&&parent->fangxaing!
=U)
{
next(parent,&child,D);
EnQueue(Q,child);
}
if(x>1&&parent->fangxaing!
=D)
{
next(parent,&child,U);
EnQueue(Q,child);
}
if(y<3&&parent->fangxaing!
=L)
{
next(parent,&child,R);
EnQueue(Q,child);
}
if(y>1&&parent->fangxaing!
=R)
{
next(parent,&child,L);
EnQueue(Q,child);
}
return1;
}
intsearch(chara[],char**path)
{
LinkQueuem,n;//分别用于从初始状态,以及末状态同步开始的两路搜索
Linklistl1,l2,temp1,temp2;
Linklist*hxb;//哈希表
hxb=(Linklist*)calloc(362881,sizeof(Linklist));
hxb[0]=(Linklist)malloc(sizeof(LNode));
hxb[0]->next1=0;
intflag1=1,flag2=1,i,j,k;//找到结果时flag=0;i,j,k作为计数量使用
char*b="123456789";
InitLNode(&l1,a,0);//初始化节点l1,l2
InitLNode(&l2,b,1);
InitQueue(&m);//初始化队列m,n
InitQueue(&n);
EnQueue(&m,l1);//l1,l2入队列
EnQueue(&n,l2);
while(flag1&&flag2)
{
dequeue(&n,&temp2);
flag2=bfs(&n,temp2,hxb);
dequeue(&m,&temp1);
flag1=bfs(&m,temp1,hxb);
}
if(0==flag1)
{
i=f(temp1->data);
(*path)=(char*)malloc(strlen(temp1->path)+strlen(hxb[i]->path));
strcpy((*path),temp1->path);
for(j=strlen(temp1->path),k=strlen(hxb[i]->path)-1;k>=0;j++,k--)
(*path)[j-1]=hxb[i]->path[k];
}
else
{
i=f(temp2->data);
(*path)=(char*)malloc(strlen(temp2->path)+strlen(hxb[i]->path)+1);
strcpy((*path),hxb[i]->path);
for(j=strlen(hxb[i]->path),k=strlen(temp2->path)-1;k>=0;j++,k--)
(*path)[j-1]=temp2->path[k];
}
(*path)[j-1]=0;
DestroyQueue(&m);
DestroyQueue(&n);
Destroylist(hxb[0]);
return1;
}
voidmove(char*data,char*path)
{
intx,y,m,n,tem,a,b;//x,y,m,n用于计算光标位置,a储存当前空格所在,b储存空格将要移动位置
char*temp;
temp=data;
m=30;
n=5;
HideCursor();//隐藏光标
for(tem=0;tem<9;tem++)//找到空格所在位置
if(temp[tem]=='9')break;
temp[tem]='';
tem=n;
gotoxy(m,n++);printf("动态演示:
");
gotoxy(m,n++);printf("┌─┬─┬─┐");
gotoxy(m,n++);printf("││││");
gotoxy(m,n++);printf("├─┼─┼─┤");
gotoxy(m,n++);printf("││││");
gotoxy(m,n++);printf("├─┼─┼─┤");
gotoxy(m,n++);printf("││││");
gotoxy(m,n++);printf("└─┴─┴─┘");
n=tem;
for(x=1;x<4;x++)
for(y=1;y<4;y++)
{
gotoxy((m-1)+4*y,n+2*x);
printf("%c",*temp++);
}
a=(*path)-48;
path++;
while((*path)!
=0)
{
Sleep(1500);
b=(*path)-48;
s[a],&data[b]);
a=b;
path++;
temp=data;
for(x=1;x<4;x++)
for(y=1;y<4;y++)
{
gotoxy((m-1)+4*y,n+2*x);
printf("%c",*temp++);
}
}
}
voidprint(char*path)
{
intx,y,m,i=0;
while((*path)!
=0)
{
if((i%3)==0)printf("\n");
m=(*path)-48;
y=m%3+1;x=m/3+1;
printf("(%d,%d)",x,y);
path++;
i++;
}
printf("\n");
system("Pause");
}