poj 1077 Eight (bfs+康拓展开,A*&&IDA*算法)
http://poj.org/problem?id=1077
一道经典到不能再经典的搜做题,留到了今天,终于过了,好开心!!!
这题可以用很多中方法来做,例如bfs,dfs,A*,IDA*什么的。下面是我用普通bfs加上康拓展开来做的代码,800+ms险过的。
View Code
1 bool used[N]; 2 char elem[10] = "12345678x"; 3 const char *end = "12345678x"; 4 5 int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; 6 template <class T> 7 int getPos(T *arr, int len) { 8 int ret, cnt; 9 VBL vis(len, false); 10 ret = 1; 11 REP(i, len) { 12 cnt = 0; 13 FORI(j, i + 1, len) { 14 if (*(arr + i) > *(arr + j)) cnt++; 15 } 16 ret += cnt * fac[len - i - 1]; 17 } 18 return ret; 19 } 20 template <class T> 21 bool getArray(T *arr, int len, int k) { 22 if (k < 1 || k > fac[len]) return false; 23 int pos = 0, num, cnt; 24 T *tmp = new T[len]; 25 VBL vis(len, false); 26 REP_1(i, len - 1) { 27 num = (k - 1) / fac[len - i] + 1, cnt = 0; 28 REP(j, len) { 29 if (!vis[j]) cnt++; 30 if (cnt == num) { 31 tmp[pos++] = arr[j]; 32 vis[j] = true; 33 break; 34 } 35 } 36 k -= (num - 1) * fac[len - i]; 37 } 38 REP(i, len) { 39 if (!vis[i]) tmp[pos++] = arr[i]; 40 } 41 REP(i, len) arr[i] = tmp[i]; 42 return true; 43 } 44 45 46 char st[10]; 47 bool input() { 48 char *p = st; 49 char buf[3]; 50 REP(i, 9) { 51 if (!(cin >> buf)) return false; 52 *(p++) = *buf; 53 } 54 // cout << st << endl; 55 *p = 0; 56 return true; 57 } 58 59 char q[N][10], op[N], ans[N]; 60 int qh, qt; 61 int last[N]; 62 63 void find(char *s, int &x, int &y) { 64 REP(i, 9) if (*(s + i) == 'x') { 65 x = i % 3, y = i / 3; 66 break; 67 } 68 } 69 70 int bfs() { 71 qh = qt = 0; 72 _clr(used); 73 strcpy(q[qt++], st); 74 int mk, x, y, cnt = 0, idx; 75 while (qh < qt) { 76 mk = qt; 77 while (qh < mk) { 78 // cout << q[qh] << endl; 79 // cout << qh << endl; 80 if (!strcmp(q[qh], end)) return cnt; 81 find(q[qh], x, y); 82 if (0 <= x - 1 && x - 1 < 3) { 83 swap(q[qh][y * 3 + x], q[qh][y * 3 + x - 1]); 84 idx = getPos(q[qh], 9); 85 if (!used[idx]) { 86 used[idx] = true; 87 strcpy(q[qt], q[qh]); 88 last[qt] = qh; 89 op[qt++] = 'l'; 90 } 91 swap(q[qh][y * 3 + x], q[qh][y * 3 + x - 1]); 92 } 93 if (0 <= x + 1 && x + 1 < 3) { 94 swap(q[qh][y * 3 + x], q[qh][y * 3 + x + 1]); 95 idx = getPos(q[qh], 9); 96 if (!used[idx]) { 97 used[idx] = true; 98 strcpy(q[qt], q[qh]); 99 last[qt] = qh; 100 op[qt++] = 'r'; 101 } 102 swap(q[qh][y * 3 + x], q[qh][y * 3 + x + 1]); 103 } 104 if (0 <= y - 1 && y - 1 < 3) { 105 swap(q[qh][y * 3 + x], q[qh][(y - 1) * 3 + x]); 106 idx = getPos(q[qh], 9); 107 if (!used[idx]) { 108 used[idx] = true; 109 strcpy(q[qt], q[qh]); 110 last[qt] = qh; 111 op[qt++] = 'u'; 112 } 113 swap(q[qh][y * 3 + x], q[qh][(y - 1) * 3 + x]); 114 } 115 if (0 <= y + 1 && y + 1 < 3) { 116 swap(q[qh][y * 3 + x], q[qh][(y + 1) * 3 + x]); 117 idx = getPos(q[qh], 9); 118 if (!used[idx]) { 119 used[idx] = true; 120 strcpy(q[qt], q[qh]); 121 last[qt] = qh; 122 op[qt++] = 'd'; 123 } 124 swap(q[qh][y * 3 + x], q[qh][(y + 1) * 3 + x]); 125 } 126 qh++; 127 } 128 cnt++; 129 } 130 return -1; 131 } 132 133 void print(int l) { 134 int rec = qh; 135 ans[l] = 0; 136 while (l--) { 137 ans[l] = op[rec]; 138 rec = last[rec]; 139 } 140 cout << ans << endl; 141 } 142 143 int main() { 144 // freopen("in", "r",stdin); 145 while (input()) { 146 int len = bfs(); 147 // cout << len << endl; 148 if (~len) print(len); 149 else puts("unsolvable"); 150 } 151 return 0; 152 }
A*,IDA*等方法之后将补充上去。
UPD:对于HDU 1043,预处理BFS的所有情况,250ms通过:(这种方法用于POJ 1077会超时)
View Code
1 int fac[15]; 2 void preFac() { 3 fac[0] = 1; 4 REP_1(i, 10) fac[i] = fac[i - 1] * i; 5 } 6 7 template <class T> 8 int getPos(T *arr, int len) { 9 int ret = 1, cnt; 10 VBL vis(len, false); 11 REP(i, len) { 12 cnt = 0; 13 INC(j, i + 1, len) if (*(arr + i) > *(arr + j)) cnt++; 14 ret += cnt * fac[len - i - 1]; 15 } 16 return ret; 17 } 18 19 bool used[N]; 20 int fth[N], qh, qt; 21 char st[10], dr[N], Q[N][10]; 22 23 bool valid(int x, int y) { 24 if (0 <= x && x <= 2 && 0 <= y && y <= 2) return true; 25 return false; 26 } 27 28 const int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; 29 const char dirch[4] = {'d', 'u', 'r', 'l'}; 30 31 void fxy(char *str, int &x, int &y) { 32 int i = 0; 33 while (*(str++) != 'x') i++; 34 x = i % 3; 35 y = i / 3; 36 } 37 38 void preBFS() { 39 preFac(); 40 qh = qt = 0; 41 int x, y, num; 42 int nx, ny; 43 REP(i, 8) { 44 st[i] = i + '1'; 45 } 46 st[8] = 'x'; 47 strcpy(Q[qt++], st); 48 used[getPos(st, 9)] = true; 49 while (qh < qt) { 50 strcpy(st, Q[qh++]); 51 // puts(st); 52 fxy(st, x, y); 53 // cout << x << ends << y << endl; 54 int tmp = getPos(st, 9); 55 REP(i, 4) { 56 nx = x + dir[i][0]; 57 ny = y + dir[i][1]; 58 if (!valid(nx, ny)) continue; 59 swap(st[y * 3 + x], st[ny * 3 + nx]); 60 num = getPos(st, 9); 61 // puts(st); 62 // cout << num << "??" << endl; 63 if (!used[num]) { 64 strcpy(Q[qt++], st); 65 used[num] = true; 66 fth[num] = tmp; 67 dr[num] = dirch[i]; 68 } 69 swap(st[y * 3 + x], st[ny * 3 + nx]); 70 } 71 } 72 } 73 74 bool input() { 75 char buf[3]; 76 REP(i, 9) { 77 if (scanf("%s", buf) == -1) return false; 78 st[i] = buf[0]; 79 } 80 return true; 81 } 82 83 void print() { 84 int cur = getPos(st, 9); 85 // cout << cur << endl; 86 if (!used[cur]) { 87 printf("unsolvable"); 88 return ; 89 } 90 while (fth[cur]) { 91 putchar(dr[cur]); 92 cur = fth[cur]; 93 } 94 } 95 96 int main() { 97 preBFS(); 98 while (input()) { 99 print(); 100 puts(""); 101 } 102 return 0; 103 }
UPD:
A*算法:
http://www.java3z.com/cwbwebhome/article/article2/2825.html
这是我认为解释A*算法解释的最生动的一个网页之一,通过这个网站上的flash演示,可以更容易理解A*算法。
下面是用A*算法的通过代码,搞这个代码debug用了我挺长的时间的,原因是不小心将方向还有输出顺序搞混了。HDU上跑,接近3000ms,POJ上跑,大约110ms。
View Code
1 int fac[15]; 2 void preFac() { 3 fac[0] = 1; 4 REP_1(i, 10) fac[i] = fac[i - 1] * i; 5 } 6 7 template <class T> 8 int getPos(T *arr, int len) { 9 int ret = 1, cnt; 10 REP(i, len) { 11 cnt = 0; 12 INC(j, i + 1, len) if (*(arr + i) > *(arr + j)) cnt++; 13 ret += cnt * fac[len - i - 1]; 14 } 15 return ret; 16 } 17 18 bool valid(int x, int y) { 19 if (0 <= x && x <= 2 && 0 <= y && y <= 2) return true; 20 return false; 21 } 22 23 const int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; 24 const char dirch[4] = {'u', 'd', 'l', 'r'}; 25 26 void fxy(char *str, int &x) { 27 int i = 0; 28 while (*(str++) != '9') i++; 29 x = i; 30 } 31 32 int next[9][4]; 33 34 void preNext() { 35 REP(i, 9) { 36 REP(j, 4) { 37 if (valid(i % 3 + dir[j][0], i / 3 + dir[j][1])) { 38 next[i][j] = i + dir[j][0] + dir[j][1] * 3; 39 } else next[i][j] = -1; 40 } 41 } 42 } 43 44 bool input(char *st) { 45 char buf[3]; 46 REP(i, 9) { 47 if (scanf("%s", buf) == -1) return false; 48 st[i] = buf[0]; 49 if (st[i] == 'x') st[i] = '9'; 50 } 51 return true; 52 } 53 54 bool vis[N]; 55 int f[N]; 56 char recDir[N]; 57 int pos[9][2], minStep[N]; 58 59 void prePos() { 60 REP(i, 9) { 61 pos[i][0] = i % 3; 62 pos[i][1] = i / 3; 63 } 64 } 65 66 int h(char *st) { 67 int ret = 0; 68 REP(i, 9) { 69 if (*st != '9') ret += abs(i % 3 - pos[*st - '1'][0]) + abs(i / 3 - pos[*st - '1'][1]); 70 st++; 71 } 72 return ret; 73 } 74 75 bool isSovlable(char *st) { 76 int t; 77 fxy(st, t); 78 t = t % 3 + t / 3; 79 REP(i, 9) { 80 REP(j, i) { 81 if (st[i] > st[j]) t++; 82 } 83 } 84 // cout << t << endl; 85 return (t & 1) == 0; 86 } 87 88 bool AStar(char *st) { 89 if (!isSovlable(st)) return false; 90 PIIS tmp; 91 PRIQ<PIIS, vector<PIIS>, greater<PIIS> > PQ; 92 int idx = getPos(st, 9), x; 93 char stTmp[10]; 94 95 _clr(f); 96 _clr(recDir); 97 _clr(vis); 98 _clr(minStep); 99 while (!PQ.empty()) PQ.pop(); 100 vis[idx] = true; 101 PQ.push(MPR(MPR(0 + h(st), 0), st)); 102 103 while (!PQ.empty()) { 104 tmp = PQ.top(); 105 PQ.pop(); 106 strcpy(stTmp, tmp.SE.c_str()); 107 // cout << stTmp << endl; 108 // cout << tmp.FI.FI << endl; 109 int cur = getPos(stTmp, 9); 110 if (cur == 1) return true; 111 fxy(stTmp, x); 112 REP(i, 4) { 113 if (next[x][i] == -1) continue; 114 swap(stTmp[x], stTmp[next[x][i]]); 115 idx = getPos(stTmp, 9); 116 if (!vis[idx] || minStep[idx] > tmp.FI.SE + 1) { 117 vis[idx] = true; 118 f[idx] = cur; 119 recDir[idx] = dirch[i]; 120 PQ.push(MPR(MPR(tmp.FI.SE + 1 + h(stTmp), tmp.FI.SE + 1), stTmp)); 121 minStep[idx] = tmp.FI.SE + 1; 122 } 123 swap(stTmp[x], stTmp[next[x][i]]); 124 } 125 } 126 return false; 127 } 128 129 int main() { 130 // freopen("in", "r", stdin); 131 // freopen("out", "w", stdout); 132 char buf[10]; 133 preFac(); 134 prePos(); 135 preNext(); 136 while (input(buf)) { 137 stack<char> out; 138 if (AStar(buf)) { 139 int tmp = 1; 140 while (f[tmp]) { 141 out.push(recDir[tmp]); 142 tmp = f[tmp]; 143 } 144 while (!out.empty()) { putchar(out.top()); out.pop();} 145 puts(""); 146 } else { 147 puts("unsolvable"); 148 } 149 } 150 return 0; 151 } 152 153 154 /* 155 1 2 3 4 5 6 x 7 8 156 1 2 3 x 5 6 4 7 8 157 */
UPD:
IDA*算法:
加深迭代搜索的A*算法是其实就是一个dfs,只不过和dfs不同的是搜索过程中会限定f(x)=g(x)+h(x)的值。虽然搜索过程中会重复,但是上限剪枝极大的弥补了这个不足,所以跑起来会十分的省时。代码在POJ上跑了16ms,在HDU上是400ms。
View Code
1 inline bool inBoard(int x, int y) { 2 return 0 <= x && x <= 2 && 0 <= y && y <= 2; 3 } 4 5 int next[9][4]; 6 const int dir[4][2] = { {1, 0}, {0, -1}, {-1, 0}, {0, 1} }; 7 const char dirCH[4] = { 'r', 'u', 'l', 'd' }; 8 9 void preNext() { 10 int nx, ny; 11 REP(i, 9) { 12 REP(j, 4) { 13 nx = i % 3 + dir[j][0]; 14 ny = i / 3 + dir[j][1]; 15 if (inBoard(nx, ny)) next[i][j] = nx + ny * 3; 16 else next[i][j] = -1; 17 } 18 } 19 } 20 21 const char *endST = "12345678x"; 22 inline bool isFinish(char *str) { 23 return !strcmp(str, endST); 24 } 25 26 inline bool canSolve(char *str) { 27 int cnt = 0; 28 REP(i, 9) { 29 if (str[i] == 'x') cnt += i / 3 + i % 3; 30 REP(j, i) { 31 if (str[i] < str[j]) cnt++; 32 } 33 } 34 return (cnt & 1) == 0; 35 } 36 37 inline int h(char *str) { 38 int ret = 0; 39 REP(i, 9) { 40 if (str[i] == 'x') continue; 41 ret += abs((str[i] - '1') % 3 - i % 3) + abs((str[i] - '1') / 3 - i / 3); 42 } 43 return ret; 44 } 45 46 inline int findPos(char *str) { 47 REP(i, 9) if (*(str++) == 'x') return i; 48 } 49 50 bool input(char *str) { 51 char buf[3]; 52 REP(i, 9) { 53 if (scanf("%s", buf) == -1) return false; 54 *(str++) = buf[0]; 55 } 56 *str = 0; 57 return true; 58 } 59 60 char recPath[N]; 61 bool IDAS(int cur, int end, char *str) { 62 if (isFinish(str)) { 63 recPath[cur] = '*'; 64 return true; 65 } 66 if (cur >= end) return false; 67 int p = findPos(str); 68 REP(i, 4) { 69 if (cur && abs(recPath[cur - 1] - i) == 2) continue; 70 if (~next[p][i]) { 71 swap(str[p], str[next[p][i]]); 72 if (h(str) + cur + 1 <= end) { 73 recPath[cur] = i; 74 if (IDAS(cur + 1, end, str)) return true;; 75 } 76 swap(str[p], str[next[p][i]]); 77 } 78 } 79 return false; 80 } 81 82 int main() { 83 char buf[15]; 84 preNext(); 85 while (input(buf)) { 86 if (!canSolve(buf)) { 87 puts("unsolvable"); 88 continue; 89 } 90 int upBound = h(buf); 91 while (!IDAS(0, upBound, buf)) { 92 upBound++; 93 // cout << upBound << endl; 94 } 95 // cout << minStep << endl; 96 char *p = recPath; 97 while (*p != '*') { 98 putchar(dirCH[*(p++)]); 99 } 100 puts(""); 101 } 102 return 0; 103 }
三种方法相对比,个人认为IDA*相对比较简洁易懂,代码复杂度也是相对低一些的!再加上程序跑的飞起,完胜另外两种方法了。
——written by Lyon