如何测试算法
首先要把所有可能的序列都输出来,其实也就是生成全排列了。这里直接给出代码。
用以下代码生成12345678x, 至x87654321
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #define MAX 100 using namespace std; int rec[MAX]; char dst[MAX]; const int frac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; char *GetString(const int v, const int MAXN = 9) //给定序列值,生成一个字符串,这个值应该是从0~(9! - 1) { int idx = 1; int value = v; memset(rec, 0, sizeof(rec)); memset(dst, 0, sizeof(dst)); do { rec[idx] = value % (idx + 1); value /= (idx + 1); idx++; } while (value); for (int now_value = MAXN, iter = MAXN - 1; now_value > 0; --now_value, --iter) { const int pos = rec[iter]; int cont = -1; for (int j = MAXN - 1; j >= 0; --j) { if (0 == dst[j]) { ++cont; if (pos == cont) { dst[j] = now_value; break; } } } } return dst; } int GetNumber(const char* dst, const int MAXN = 9) //从相应的字符串,得到序列值。 { int iNumber = 0; for (int i = 0; i < MAXN - 1; ++i) { int temp = 0; for (int j = i + 1; j < MAXN; ++j) { if (dst[j] < dst[i]) { ++temp; } } iNumber += frac[dst[i] - 1] * temp; } return iNumber; } char _str[100]; int main(void) { for (int i = 0; i < frac[9]; ++i) { char *str = GetString(i), *p = str; while (*str) { *str += '0'; if (*str == '9') *str = 'x'; ++str; } printf("%s\n", p); } return 0; }
当你的算法生成处理结果之后,那么比如得到字符串:
输入:
23415x768
输出:
ullddrurdllurdruldr如果能够按照这个序列进行移动,能够移动至0序号的状态,那么就是对的了。
下面给出移动的代码:
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<algorithm> using namespace std; char map[3][3]; char str[100]; char oper[100]; void print() { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { printf("%c", map[i][j]); } printf("\n"); } } int main(void) { int iter = -1, sx = 0, sy = 0; while (1) { gets(str); gets(oper); for (char *p = str; *p; ++p) { if (!('0' <= *p && *p <= '9') && 'x' != *p && 'X' != *p) continue; ++iter; map[iter/3][iter%3] = *p; if (*p == 'x') { sx = iter / 3; sy = iter % 3; } } print(); printf("center = %d, %d\n", sx, sy); for (char *p = oper; *p; ++p) { switch(*p) { case 'l': std::swap(map[sx][sy], map[sx][sy-1]); --sy; break; case 'r': std::swap(map[sx][sy], map[sx][sy+1]); ++sy; break; case 'u': std::swap(map[sx][sy], map[sx-1][sy]); --sx; break; case 'd': std::swap(map[sx][sy], map[sx+1][sy]); ++sx; break; } } print(); } return 0; }
设置Hash表
一般而言,给定的十进制数有987654321,如果直接使用一个数组来记录状态,会超出内存的,需要压缩状态。
在这里是把x当成9来处理。
由于每种排列与序号对应,可以把这个逆序数来做为hash函数。
int arr[20]; int GetNumber(const int value) { const int frac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; int iNumber = 0, temp = 0, i, j, xi, *t = (int *)p; for (i = 0; i < 9; ++i, ++t) arr[8 - i] = (value % *(t+1)) / *t; for (i = 0; i < 8; ++i) { for (xi = arr[i], j = i + 1; j < 9; ++j) { (arr[j] < xi) ? iNumber += frac[xi - 1] : 0; } } return iNumber; }
#define BITSPERWORD 32 #define SHIFT 5 #define MASK 0x1F #define N 362890 int a[(N>>SHIFT) + 10]; inline void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); } inline void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); } inline int test(int i){ return a[i>>SHIFT] & (1<<(i & MASK)); }
在这里,很多人都是用的是char 数组来实现位变换,我这里用的是直接用十进制来进行操作。
位数编码如下:
123456789 = 值
012345678 = 位数
const int _p10[] = { 1, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1000000000 }; const int *p = _p10 + 1; static void _swap(int *v, int x, int y) { int i = 8 - x, j = 8 - y, xi, xj; int pi = p[i], pj = p[j], pi1 = p[i+1], pj1 = p[j + 1]; x = *v % pi1, y = *v % pj1; xi = x / pi; xj = y / pj; *v = *v - x + xj * pi + x % pi; *v = *v - y + xi * pj + y % pj; }
int main(void) { int x = 987654321; _swap(&x, 0,8); printf("%d\n", x); return 0; }
如何广搜
我的办法是从0序号开始,把所有点走一下。然后再进行判断。
需要注意的是,大致思路是记录十进制的值。按十进制应该是从123456789开始。
然后把每个点的后继值都要入栈。在得到后继结点的时候,需要确定向哪个方向翻转,
我采用的是udlr的方向。那么这里需要一个数组dk[][4]来记录当中心位于i时,需要向dk[i]所指向的四个方向,如果为-1,则表示那个方面没有值。不用向那个方向翻转。
比如位于位置0,则需要向:
[位置图]
0 1 2
3 4 5
6 7 8
上 下 左 右
-1 3 -1 1
这么四个方向进行操作。
这里需要生成一个类似于:
const int dk[][4] = {
{-1, 3, -1, 1, },
{-1, 4, 0, 2, },
{-1, 5, 1, -1, },
{0, 6, -1, 4, },
{1, 7, 3, 5, },
{2, 8, 4, -1, },
{3, -1, -1, 7, },
{4, -1, 6, 8, },
{5, -1, 7, -1, },
};
这样的一个矩阵来记录不同的中心位置,应该向哪些方向翻转。
矩阵生成代码如下:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> int dk[][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; char map[3][3]; void print() { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { printf("%c", map[i][j]); } printf("\n"); } } int main(void) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { int iter = i * 3 + j; map[i][j] = iter + '0'; } } print(); for (int i = 0; i < 9; ++i) { printf("{"); for (int j = 0; j < 4; ++j) { int x = i / 3 + dk[j][0]; int y = i % 3 + dk[j][1]; if (x <0 || x > 2 || y < 0 || y > 2) printf("-1, "); else printf("%c, ", map[x][y]); } printf("},\n"); } return 0; }
int q[N]; int qv[N]; //qv是用来记录入栈所对应的hash值,这样的话,只有在入栈的时候需要计算一下hash值。 int front[N]; char oper[N]; char center[N]; void BFS(int t) { int head = -1, tail = -1, next = 0; int old_value, old_center, new_value, new_center, i; q[++tail] = t; qv[tail] = GetNumber(t); while (head != tail) { t = q[++head]; old_value = qv[head]; old_center = center[old_value]; for (i = 0; i < 4; ++i) { new_center = dk[old_center][i]; if (-1 != new_center) { next = t; _swap(&next, old_center, new_center); new_value = GetNumber(next); if (0 == test(new_value)) { set(new_value); front[new_value] = old_value; oper[new_value] = i; center[new_value] = new_center; q[++tail] = next; qv[tail] = new_value; } } } } } char _output[N]; void PrintPath(int start) { int idx = 0; char *rec = _output; if (!start) {puts(""); return;} while (start != 0) { *rec++ = GetFrontOperChar(oper[start]); start = front[start]; } *rec = 0; puts(_output); }
memset(oper, -1, sizeof(oper)); center[0] = 8; front[0] = -1; oper[0] = 0; set(0); BFS(123456789);