参考白书的方法,将每一个状态映射到一个9位整数,然后再映射到它在所有状态中的大小位置(编码)来减少内存使用;
按照 POJ eight这道题的输入形式写的(对空格的处理参考了Staigner大牛的做法:用scanf一个字符串来读取);
输入包括初始状态和目标状态,输出为最短距离,如果无解,输出 -1;
逆序的剪枝比较难理解:当前状态逆序数+已走步数与目标态逆序数同奇偶则有解,有解状态不会扩展出无解状态,反之也成立,初始状态的逆序数+初始态与目标态之间空格的Manhattan距离与目标态同奇偶择有解,反之无解;
LJ 大牛提出了另一种剪枝的方法:将目标态中任意非零数互换位置得到的状态是无解状态可以达到的目标态,因此在搜索时,扩展到这些目标态表明当前状态无解;
BFS的标记写在了出队时导致了运行速度过慢(一个31步的数据跑了1s),我一直都是这样写BFS的……多亏了LJ大牛,才降到了400ms左右!
# include <stdio.h># include <mem.h># include <time.h># include <math.h> # define N 362880+10 typedef struct{ char a[9]; }state; int fact[9]; charvis[N]; intdis[N]; state Q[N]; const int d[4][2] = {{-1,0}, {0,1}, {1,0}, {0,-1}}; void read(state*s); intequal(state s1, state s2); int compu_fact(void); intcode(state s); intrev_num(state s); intbfs(state start, state goal); intmanh_dis(state s1, state s2); intmain() { inti, j; state start, goal; freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); compu_fact(); read(&start); read(&goal); /*if ((rev_num(start)+manh_dis(start, goal))%2 != rev_num(goal)%2) printf("-1\n");
这里的剪枝不太理解,总是把可解的状态也剪掉了 */printf("%d\n", bfs(start, goal)); printf("time used %.3lfs.\n", (double)clock()/CLOCKS_PER_SEC); return 0; } void read(state*s) { inti; char ch[5]; for (i = 0; i < 9; ++i) { scanf("%s", ch); if (ch[0] == 'x') (*s).a[i] = 0; else (*s).a[i] = ch[0] - '0'; } } intbfs(state start, state goal) { intfront, rear; intnx, ny, x, y; inti, code_t, code_nt; state t, nt; memset(vis, 0, sizeof(vis)); memset(dis, -1, sizeof(dis)); front = 1; rear = 2; Q[front] =start; dis[(code(start))] = 0; while (front <rear) { t = Q[front++]; code_t =code(t); if (equal(t, goal)) returndis[code_t]; for (i = 0; t.a[i]!=0 && i < 9; ++i) ; x = i / 3; y = i % 3; for (i = 0; i < 4; ++i) { nx = x + d[i][0]; ny = y + d[i][1]; if (nx>=0 && nx<3 && ny>=0 && ny<3) { nt =t; nt.a[3*x+y] = t.a[nx*3+ny]; nt.a[nx*3+ny] = 0; code_nt =code(nt); if (!vis[code_nt]) { vis[code_nt] = 1; Q[rear++] =nt; dis[code_nt] = dis[code_t] + 1; } } } } return -1; } intcode(state s) { inti, j, cnt, ret; ret = 0; for (i = 0; i < 9; ++i) { cnt = 0; for (j = i+1; j < 9; ++j) if (s.a[j] < s.a[i]) ++cnt; ret += fact[8-i]*cnt; } returnret; } int compu_fact(void) { inti; fact[0] = 1; for (i = 1; i < 9; ++i) fact[i] = fact[i-1]*i; } intequal(state s1, state s2) { inti; for (i = 0; i < 9; ++i) if (s1.a[i] != s2.a[i]) return 0; return 1; } intrev_num(state s) { inti, j, ret; ret = 0; for (i = 0; i < 9; ++i) for (j = i+1; j < 9; ++j) if (s.a[i] > s.a[j]) ++ret; returnret; } intmanh_dis(state s1, state s2) { inti, j; for (i = 0; s1.a[i]!=0 && i<9; ++i) ; for (j = 0; s1.a[j]!=0 && j<9; ++j) ; return abs(i/3 - j/3) + abs(i%3 - j%3); }
此题已做,人生更加完整了,接下来几天打算试试别的方法(学学A*等启发式方法)。