#include<stdio.h>#include<string.h>#include<stdlib.h>#include <windows.h>#include"Basic_Operation.h"#define U 56//up#define D 57//down#define L 58//left#define R 59//righttypedef struct LNode{int data;//用一个各位不相等的9位数来表示当前状态,9表示空格int flag;//0表示由初始状态生成,1表示由末状态生成int fangxaing;//表示双亲结点生成此结点时空格的移动方向char *path;//存放路径的数组下标,比实际值小1struct LNode *next,*next1;//next用于队列中,next1用于链表}LNode,*Linklist;typedef struct {Linklist front,rear;}LinkQueue,*Queue;void gotoxy(int x, int y){int xx=0x0b;HANDLE hOutput;COORD loc;loc.X=x;loc.Y=y;hOutput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOutput, loc);return;}void HideCursor(){CONSOLE_CURSOR_INFO cursor_info = {1, 0};SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); }int InitQueue(Queue Q){Q->front=(Linklist)malloc(sizeof(LNode));Q->rear=Q->front;return 1;}int EnQueue(Queue Q,Linklist tem){Q->rear->next=tem;Q->rear=tem;return 0;}int dequeue(Queue Q,Linklist *tem) {*tem=Q->front->next;Q->front=Q->front->next;return 0;}int DestroyQueue(Queue Q){Linklist tem,tem1;if(Q->front==Q->rear)return 0;dequeue(Q,&tem);while(Q->front!=Q->rear){tem1=Q->front;dequeue(Q,&tem);free(tem1->path);free(tem1);}free(Q->rear->path);free(Q->rear);return 0;}int Destroylist(Linklist a){Linklist tem;while(a->next1!=0){tem=a;a=a->next1;free(tem->path);free(tem);}return 1;}void swap(char *a,char *b){char tem;tem=*a;*a=*b;*b=tem;}int InitLNode(Linklist *M,char *b,int n){int temp;(*M)=(Linklist)malloc(sizeof(LNode));sscanf(b,"%d",&(*M)->data);(*M)->path=(char*)malloc(2*sizeof(char));for(temp=0;temp<9;temp++)//找到空格所在位置if(b[temp]=='9')break;(*M)->path[0]=temp+48;(*M)->path[1]=0;(*M)->flag=n;(*M)->fangxaing=0;return 0;}int check(char a[]);//检测初始状态是否有解int search(char a[],char **path);//寻找解。
将路径存入pathvoid print(char *path);//输出路径void move(char *data,char *path);//动态演示int main(void){int flag=1;//flag为0时退出char a[9]={0};char b[9]={0};char *path;char tem;while(flag){printf("请以字符串形式输入九宫格初始状态,空格用9表示。
例如:\n");printf("初始状态┌─┬─┬─┐\n");printf("\t│ 1│ 2│ 3│\n");printf("\t├─┼─┼─┤\n");printf("\t│ 4│ 5│ 6│\n");printf("\t├─┼─┼─┤\n");printf("\t│ 7│ 8││\n");printf("\t└─┴─┴─┘\n");printf("输入\"123456789\"\n");scanf("%s",a);getchar();//把回车吃掉strcpy(b,a);if(check(b)){printf("unsolvable\n");printf("输入Y测试下一个九宫格,输入其他任意键退出\n");}else{printf("空格所经路径为\n");search(a,&path);print(path);move(a,path);gotoxy(30,13);printf("输入Y测试下一个九宫格,输入其他任意键退出\n");gotoxy(30,14);}tem=getchar();getchar();//把回车吃掉if(tem=='y'||tem=='Y'){system("cls");}else{flag=0;}}return 0;}int check1(int n,char a[]){int i,m=0;for(i=0;i<n;i++)if(a[i]>a[n])m++;return m;}int check(char a[]){int i,tem=0;for(i=0;i<9;i++)//找到空格所在位置if(a[i]=='9')break;while(i<6){swap(&a[i],&a[i+3]);i=i+3;}while(i<8){swap(&a[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);}return tem%2;}void nextpath(Linklist parent,Linklist child,int n){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;}int next(Linklist parent,Linklist *child,int flag){char tem[10]={0};int temp;sprintf(tem,"%d",parent->data);*child=(Linklist)malloc(sizeof(LNode));for(temp=0;temp<9;temp++)//找到空格所在位置if(tem[temp]=='9')break;switch(flag){caseU:swap(&tem[temp],&tem[temp-3]);nextpath(parent,*child,temp-3);break;caseD:swap(&tem[temp],&tem[temp+3]);nextpath(parent,*child,temp+3);break;caseL:swap(&tem[temp],&tem[temp-1]);nextpath(parent,*child,temp-1);break;caseR:swap(&tem[temp],&tem[temp+1]);nextpath(parent,*child,temp+1);break;}(*child)->flag=parent->flag;sscanf(tem,"%d",&((*child)->data));(*child)->fangxaing=flag;return 0;}int f(int n){//哈希函数int m=0,i,a[8]={40320,5040,720,120,24,6,2,1};char tem[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)/1 000000)-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);return m+1;}int inhxb(Linklist tem,Linklist a[]){//哈希函数为所有比表示这个状态的各位不相等的九位数小的各位不相等的九位数的个数,所以不会产生冲突//将tem放入正确的位置,并利用结点中的next构造一个头结点为hxb[0]的单链表便于之后释放空间int n,m;n=tem->data;m=f(n);a[m]=tem;tem->next1=a[0];a[0]=tem;return 1;}int bfs(Queue Q,Linklist parent,Linklist hxb[]){//对结点tem进行宽度优先搜索,并将子状态入队列,int m,x,y;//x,y表示空格在3*3矩阵中的位置,char temp[9];Linklist child;m=f(parent->data);if(hxb[m]!=0){if(hxb[m]->flag==parent->flag)return 1;elsereturn 0;}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);}return 1;}int search(char a[],char **path){LinkQueue m,n;//分别用于从初始状态,以及末状态同步开始的两路搜索Linklist l1,l2,temp1,temp2;Linklist *hxb;//哈希表hxb=(Linklist*)calloc(362881,sizeof(Linklist));hxb[0]=(Linklist)malloc(sizeof(LNode));hxb[0]->next1=0;int flag1=1,flag2=1,i,j,k;//找到结果时flag=0;i,j,k作为计数量使用char *b="123456789";InitLNode(&l1,a,0);//初始化节点l1,l2InitLNode(&l2,b,1);InitQueue(&m);//初始化队列m,nInitQueue(&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]);return 1;}void move(char *data,char *path){int x,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;swap(&data[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++);}}}void print(char *path){int x,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");}。