题目 重排9宫

设计内容:

编写软件模拟排九宫。从九宫的某个状态出发,移动格子中的数字,使9宫格中的数字顺序排列,空格在最后。

设计功能:

1、九宫初始化,绘制九宫初始状态。9宫格中只有一个位置是空的,其它位置分别是1-8的8个数字,

2、手工输入移动九宫的步骤,如11d(第一行一列格中数字下移),22r(第二行二列格中数字右移),23u(第二行三列格中数字上移)等,记录移动后九宫状态,并形象绘制。要求判断移动是否合法。一次只能移动一个数字,并且只能到相邻的空格子中。

3、记录从初始状态以后的移动步骤。

4、判断成功状态,并给出成功提示。

5、你能否让计算机自动求解(思考选作)。

756
82
431

初始状态

123
456
78

完成状态

程序源代码

/*
输入命令介绍 
   w  手动输入九宫格(用0表示空白格) 
   m  进入移动模式 
       11d 第一行一列格中数字下移
	   22r 第二行二列格中数字右移
	   23u 第二行三列格中数字上移
	   ...诸如此类 
	   p 输出从初始状态以后的移动步骤 
       e 退出移动模式 
   r  随机生成一个九宫格 
   f  自动求解 
   e  退出程序 
*/ 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int map[3][3];
void swap(int *a,int *b) {
	int t = *a; *a = *b; *b = t;
}

// 输出九宫格
void print_map(int map[3][3]) {
   int i,j;
   for (i = 0; i < 3; i++) {
     for (j = 0; j < 3; j++) 
        if(map[i][j]) printf("%d ",map[i][j]);
	    else printf("  ");
	 puts("");
   }
}

// 键盘输入九宫格
void write() {
   int i,j,v[10] = {0};	
   for (i = 0; i < 3; i++)
     for (j = 0; j < 3; j++) {
       scanf("%d",&map[i][j]);
	   v[map[i][j]] = 1; 
	 }
   for (i = 0; i < 9; i++) 
    if (v[i] != 1) {  
    	printf("您的输入不合法,请重试!\n");
    	write(); 
    	return;
	}
   printf("输入成功!结果如下\n");
   print_map(map);	  
}

// 随机生成一个九宫格
void random_map() {
	int i,j;
	for (i = 0; i < 3; i++)
     for (j = 0; j < 3; j++)
      map[i][j] = i*3+j;
    for (i = 0; i < 9; i++) 
      swap(&map[rand()%3][rand()%3],&map[rand()%3][rand()%3]);
    print_map(map);
}

int in_map(int x,int y) {
	return x>=0 && y>=0 && x<3 && y<3;
}

// 编码算法,把九宫格映射成一个int值(9位数)
int map_to_int(int a[3][3]) {
	int ans = 0,i,j;
	for (i = 0; i < 3; i++)
     for (j = 0; j < 3; j++) 
      ans = ans*10+a[i][j];
	return ans;
}

// 键盘输入移动方向
void move() {
	char s[5];
	int st[300][3],top = 0;
	while (~scanf("%s",s)) {
	  if(s[0] == 'e') return;
	  if(s[0] == 'p') {
  		for (int i = 0; i < top; i++) {
		   printf("第%d行%d列格中数字",st[i][0]+1,st[i][1]+1);  
		   if(st[i][2] == 0) puts("上移"); 	
		   else if(st[i][2] == 1) puts("下移"); 
		   else if(st[i][2] == 2) puts("左移"); 
		   else puts("右移"); 
	    }
	    continue; 
   	  }  
	  int dx = 2,dy;
	  switch(s[2]) {
	    case 'u': dx=-1,dy=0,st[top][2]=0; break;
		case 'd': dx=1,dy=0,st[top][2]=1;  break;
		case 'l': dx=0,dy=-1,st[top][2]=2; break;
		case 'r': dx=0,dy=1,st[top][2]=3;  break;
		defalt: printf("输入不合法!\n");
	  }
	  if(dx == 2) continue;
	  int x = s[0]-'1',y = s[1]-'1';   
	  if(!in_map(x+dx,y+dy) || !in_map(x,y)) {
	    printf("输入不合法!\n");
	    continue; 
	  } 
	  swap(&map[x][y],&map[x+dx][y+dy]);
	  st[top][0] = x; st[top++][1] = y;
	  print_map(map);
	  if(map_to_int(map) == 123456780)
	    puts("这是完成状态!!");
	}
}

#define MAXX 362883
int q[MAXX],head,tail;
int dis[MAXX],per[MAXX];
int xx[] = {1,-1,0,0};
int yy[] = {0,0,1,-1};

int next(int x) {
	return ++x == MAXX ? x = 0 : x;
}
// 九宫格9个数按照顺序排列其实就是1..9全排列,用康托展开编码
int fac[11] = {1,1,2,6,24,120,720,5040,40320,362880,3628800};
int map_to_order(int a[3][3]) {	
    int i,j,ans = 0;
	for (i = 0; i < 9; i++) {
	  int cnt = 0;
	  for (j = i+1; j < 9; j++) 
	   if (a[j/3][j%3] < a[i/3][i%3]) cnt++;
      ans += cnt*fac[8-i];
	} 
	return ans; 
}

// 9位数还原成九宫格
void int_to_map(int x,int a[3][3]) {
	int i;
	for (i = 8; ~i; i--,x /= 10)
	  a[i/3][i%3] = x%10;
}

// bfs求解算法
// 使用两种编码方法是因为9位数易于编码解码, 康托展开可以映射到更小的空间,但难解码(其实可以)
int ans[MAXX],top;
void find() {
	head = 1; tail = 2;
	q[1]  =  map_to_int(map);
    if(q[1] == 123456780) {
	  printf("无需移动!\n");
  	  return;
    }
	int a[3][3],i,x,y,k;
	for (i = 0; i < MAXX; i++)
      dis[i] = 0xffffff,per[i] = 0;
	dis[map_to_order(map)] = 0;
	while(head != tail) {
	  int_to_map(q[head],a);
	  int old_id = map_to_order(a);
	  for (x = 0; x < 3; x++)
        for (y = 0; y < 3; y++)
          for (i = 0; i < 4; i++)
            if (in_map(x+xx[i],y+yy[i])) {
  	          swap(&a[x][y],&a[x+xx[i]][y+yy[i]]);
      	      int new_id = map_to_order(a);
		      if (dis[new_id] > dis[old_id]+1) {
	 	        dis[new_id] = dis[old_id]+1;
	 	        per[new_id] = q[head];
	 	        int id_int = map_to_int(a);
	 	        if (id_int == 123456780){
	              printf("可以通过%d次变换完成\n",dis[new_id]);	
	              top = 0;
	              for (k = id_int; k; ) {
                    ans[++top] = k;
                    int_to_map(k,a);
			        k = per[map_to_order(a)];	
	              }
	              int ttop = top;
	              while(top) {
			 	  	printf("第%d次变换后为:\n",ttop-top);
                    int_to_map(ans[top],a);
                    print_map(a);puts("");
	  	            top--;
	              }
	              return;
                }
	 	        q[tail] = id_int; tail = next(tail);
		     }
		     swap(&a[x][y],&a[x+xx[i]][y+yy[i]]);
	     }
	  	 head = next(head);
	} 
}

int main() {
   srand(time(0));
   char opt;
   while(~scanf("%c",&opt)) {
     if(opt == 'e') break;
	 else if(opt == 'w') write();
	 else if(opt == 'm') move();
	 else if(opt == 'r') random_map();
	 else if(opt == 'f') find();
   }
   return 0;
}