题目描述:
代码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #define N 1000000 5 #define HN 1000003 6 #define LEN 9 7 8 int head[HN],next[N]; 9 int st[N][LEN], goal[LEN]; 10 int dis[N]; //记录步数 11 12 int Hash(int *st)//获取本次排列组合的哈希值 13 { 14 int i,v; 15 v = 0; 16 for (i=0 ; i<LEN ; i++)//得到一个LEN长度数值:v 17 { 18 v = v*10 + st[i]; 19 } 20 return v%HN;//得到对应哈希值 21 } 22 23 24 // 功能:对本次的排列组合,若不在哈希表中,则在哈希表中进行键值映射 25 int try_insert(int rear)//rear:本次组合的编号 26 { 27 int h = Hash(st[rear]); //获得本次排列组合对应的哈希值 28 int u = head[h]; //根据哈希值,获得哈希表中对应的键(key),若没有则为0 29 30 while (u)//当上一步有拿到键(key),查找对应的值(value->排列组合), 31 { 32 if (memcmp(st[u],st[rear],sizeof(st[0]))==0)//存在相同的值(value->排列组合) 33 return 0; //本次值(value->排列组合)将不记录,退出 34 u = next[u]; //当前键(key)还有其他值,遍历获得其他值(value->排列组合) 35 } 36 37 //当前值(value->排列组合)不在哈希表中,将进行记录 38 next[rear] = head[h]; //当前键(key)的记录是否有其他值,若本身为新组合,为0;若为存在组合,则为其上一个编号(相同组合) 39 head[h] = rear; //哈希表中对应的键(key),更新为本次组合编号(rear) 40 return 1; 41 } 42 43 int bfs() 44 { 45 int d; 46 int x,y,z,nx,ny,nz; 47 int fron = 1,rear = 2; //fron:当前排列组合;rear:移动后的排列组合 48 const int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0},}; 49 50 memset(head,0,sizeof(head)); 51 while (fron < rear) 52 { 53 if (memcmp(goal,st[fron],sizeof(st[0]))==0)//当前排列组合为目标排列组合 54 return fron; 55 56 for (z=0 ; z<LEN ; z++) 57 { 58 if (st[fron][z]==0)//0即为对应的'.' 59 break; 60 } 61 62 x = z/3,y = z%3;//获得'.'的坐标 63 for (d=0 ; d<4 ; d++) 64 { 65 nx = x+dir[d][0]; 66 ny = y+dir[d][1]; 67 if (nx>=0 && nx<3 && ny>=0 && ny<3) 68 { 69 nz = 3*nx+ny;//'.'将要移动的下一步位置 70 71 memcpy(&st[rear],&st[fron],sizeof(st[0])); 72 st[rear][nz] = st[fron][z]; 73 st[rear][z] = st[fron][nz]; 74 75 dis[rear] = dis[fron]+1;//记录步数 76 if (try_insert(rear))//对新排列组合进行查找 77 rear ++;//进队列 78 } 79 } 80 fron ++;//出队列 81 } 82 83 return 0; 84 } 85 86 int main(void) 87 { 88 int i,ans; 89 char s1[LEN+1],s2[LEN+1]; 90 memset(dis,0,sizeof(dis)); 91 scanf("%s%s",&s1,&s2); 92 93 for (i=0 ; i<LEN ; i++)//将'.'转换为0,便于计算哈希值 94 { 95 if (s1[i]=='.') 96 st[1][i] = 0; 97 else 98 st[1][i] = s1[i]-'0'; 99 100 if (s2[i]=='.') 101 goal[i] = 0; 102 else 103 goal[i] = s2[i]-'0'; 104 } 105 106 ans = bfs(); 107 if (ans > 0) 108 printf("%d",dis[ans]); 109 else 110 puts("-1"); 111 112 return 0; 113 }
解题思路:
本题要求最少步数转换成目标组合,广度优先遍历(BFS)可满足要求
每次对新组合进行检查时,需要较高的检索效率,
1.使用map或者set做检索时,其对应的检索效率低,超时不能满足题目需求;
2.对组合进行哈希判重,大大提高了检索效率;
关于本题的哈希判重,参考了:
https://blog.csdn.net/hao_zong_yin/article/details/62419919
https://www.cnblogs.com/acxblog/p/7253477.html
哈希判重,利用键-值映射,有效提高了搜索的效率;
1.建立键(key)表,head[NH],需初始化;建立值(value)表,next[N];
2.BFS搜索新组合,通过向不同方向移动 ' . ' (0) 得到新的组合;
3.根据新组合,计算对应的哈希值;
4.根据哈希值,进行检索决定是否添加新的排列组合
4.1.插入的组合存在
4.2.插入的组合不存在
5.若有新组合添加,队列+1(rear+1)
6.每次出列(fron+1),检查是否为目标组合
7.若fron > rear,则不存在方案,输出-1