八数码问题,逆序数判定是不是有解,需要考虑0吗?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八数码问题,逆序数判定是不是有解,需要考虑0吗?相关的知识,希望对你有一定的参考价值。
八数码问题,判定是否有解,需要考虑0吗?
九宫格上分别放入的是0-8,按从上到下,从左到右分别把数字放入一维数组里面,然后求他们的逆序数。网上有人说不要考虑0,但是我发现有点问题,不知道该听谁的,有人能肯定吗?
0代表空位本回答被提问者采纳
HDU 1043 八数码问题的多种解法
一、思路很简单,搜索。对于每一种状态,利用康托展开编码成一个整数。于是,状态就可以记忆了。
二、在搜索之前,可以先做个优化,对于逆序数为奇数的序列,一定无解。
三、搜索方法有很多。
1、最普通的:深搜、广搜。在这题里面,这两个方法直接TLE。所以,我后面没有贴超时的代码。
2、既然1超时,那就预处理出所有状态,用map存储,然而,map的insert(使用[]是一样的)实在太慢了,也超时。
3、在1的基础上,优化一下,得到:IDA*,双向广搜,A*。
3、IDA*我没尝试,不过感觉没有A*快。另外,双向广搜可以加优化,在HDU上可以快大概300ms,就是:对于某一个方向,在开始BFS时,如果当前的队列里面状态个数很多,而另外一个队列里面相对较少,那么,把搜索方面换成另一边。这样,不至于出现两个队列的状态个数相差太远以至于退化成单向BFS的情况。当然,这个题两个队列中的状态都是比较均匀的,即使不加优化,效果也不会太差。
4、不管用的是什么搜索,搜索过程中,多个样例经历经历相同的状态是非常有可能的,所以,理论上来说,可以加缓存,也就是其实不管是什么样例,只要序列确定,得到的整数编码一定是确定的,所以,理论上来说,加缓存可以加速,然而, 不知为何,这题,加了缓存还更慢。多半是因为我的缓存用的是C++ STL吧。
四、注意点
1、双向BFS加优化时,C++的queue的size()的返回值类型是unsigned int,直接相减,会发生溢出的情况, 导致size()小的队列大小一直不变,size()大的队列一直在扩大,也就是退化成了单向BFS了。T_T。所以,为了防止这种情况发生,在size相减之前,用两个int变量先存储好这两个队列的size。否则,会很坑。
2、在循环中开辟新指针,每一次循环开辟的指针值都一样,如代码所示。
#include<bits/stdc++.h> using namespace std; typedef struct Foo{ int v1, v2; Foo(int _v1,int _v2){ v1 = _v1; v2 = _v2; } Foo(){} }Node; set<Node*> s; int main(){ for(int i = 0;i < 5;++i){ Node node = Node(i, i); s.insert(&node); } printf("%d\n", s.size()); }
输出结果会是1,而不是5。特别要注意。而如果这么写:
#include<bits/stdc++.h> using namespace std; typedef struct Foo{ int v1, v2; Foo(int _v1,int _v2){ v1 = _v1; v2 = _v2; } Foo(){} }Node; set<Node*> s; int main(){ for(int i = 0;i < 5;++i){ Node* pnode = new Node(i, i); s.insert(pnode); } printf("%d\n", s.size()); }
因为C++没有自动垃圾回收,所以,很有可能会出现MLE的情况。因此,ACM竞赛中,尽可能地不要使用指针。
3、C++的容器,map、string、vector之类的,慢!慢!慢!如果条件允许,时间又压得比较紧,特别是像string这种的,最好还是用C语言原生的字符串数组,虽然麻烦些,但是比string快不只一点点。
五、源代码
1、双向BFS(队列容量均衡优化)
#include<iostream> #include<cstdio> #include<vector> #include<cstring> #include<queue> #include<algorithm> using namespace std; int facts[15]; void init() { facts[0] = facts[1] = 1; for(int i = 2; i <= 10; ++i)facts[i] = facts[i - 1] * i; } int encode(vector<int>& seq) { int res = 0; int num = seq.size(); for(int i = 0; i < num - 1; ++i) { int cnt = seq[i] - 1; for(int j = 0; j < i; ++j) { if(seq[j] < seq[i])--cnt; } res += cnt * facts[num - i - 1]; } return res; } vector<int> temp; void decode(int n, int m) { temp.clear(); int buf = 0; int i, j, r, t; for(t = n; t > 0; --t) { r = m / facts[t - 1]; m %= facts[t - 1]; for(i = 1; i <= n; ++i) { if(!((buf >> i) & 1)) { if(r == 0)break; --r; } } temp.push_back(i); buf |= 1 << i; } } char line[30]; int get() { vector<int> vec; int len = strlen(line); for(int i = 0; i < len; ++i) { if(isdigit(line[i]))vec.push_back(line[i] - ‘0‘); else if(line[i] == ‘x‘)vec.push_back(9); } int cnt = 0; for(int i = 0, sz = vec.size(); i < sz; ++i) { if(vec[i] == 9)continue; for(int j = 0; j < i; ++j) { if(vec[j] == 9)continue; if(vec[j] > vec[i])++cnt; } } if(cnt & 1)return -1; else return encode(vec); } int findPos() { for(int i = 0; i < 9; ++i)if(temp[i] == 9)return i; return -1; } typedef struct Foo { int code; char step; Foo(int l, char s) { code = l; step = s; } Foo() {} } Node; string res[2]; const int dirs[] = { -1, 1, -3, 3}; queue<int> que[2]; bool vis[2][362888]; Node pre[2][362888]; bool bfs(int s[]) { for(int i = 0; i < 2; ++i) { res[i].clear(); while(!que[i].empty())que[i].pop(); memset(vis[i], 0, sizeof(vis[i])); vis[i][s[i]] = true; for(int j = 0; j < 362888; ++j)pre[i][j].code = -1; que[i].push(s[i]); } int k = 0; while(!que[k].empty()) { int sz1 = que[k].size(), sz2 = que[k ^ 1].size(); if(sz1 - sz2 > 1)k ^= 1;//队列均衡优化 int code = que[k].front(); que[k].pop(); if(vis[k ^ 1][code]) { for(int i = 0; i < 2; ++i) { int now = code; int last = pre[i][now].code; while(last != -1) { res[i].push_back(pre[i][now].step); now = last; last = pre[i][last].code; } if(i == 0)reverse(res[i].begin(), res[i].end()); } return true; } decode(9, code); int pos = findPos(); for(int i = 0; i < 4; ++i) { int np = pos + dirs[i]; if(np < 0 || np > 8)continue; bool cond = false; if(i == 0 || i == 1)cond = (np / 3 == pos / 3); else if(i == 2 || i == 3)cond = (np >= 0 && np <= 8); if(cond) { swap(temp[np], temp[pos]); int nv = encode(temp); if(!vis[k][nv]) { if(i == 0)pre[k][nv] = Node(code, ‘l‘); else if(i == 1)pre[k][nv] = Node(code, ‘r‘); else if(i == 2)pre[k][nv] = Node(code, ‘u‘); else if(i == 3)pre[k][nv] = Node(code, ‘d‘); que[k].push(nv); vis[k][nv] = true; } swap(temp[np], temp[pos]); } } k ^= 1; } return false; } void change(string& s) { for(int i = 0, len = s.length(); i < len; ++i) { if(s[i] == ‘l‘)s[i] = ‘r‘; else if(s[i] == ‘r‘)s[i] = ‘l‘; else if(s[i] == ‘u‘)s[i] = ‘d‘; else if(s[i] == ‘d‘)s[i] = ‘u‘; } reverse(s.begin(), s.end()); } int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); #endif // ONLINE_JUDGE ios::sync_with_stdio(false); init(); while(gets(line) != NULL) { int v = get(); if(v < 0)cout << "unsolvable" << endl; else if(v == 0)cout << endl; else { int s[] = {v, 0}; if(bfs(s)) { change(res[1]); cout << res[0] << res[1] << endl; } else cout << "unsolvable" << endl; } } }
2、A*搜索。
#include<bits/stdc++.h> using namespace std; int facts[10]; char input[30]; typedef struct Foo { int code; int pos9; double g, h; Foo(int c, int p, double _g, double _h) { code = c; pos9 = p; g = _g; h = _h; } Foo() {} /*这里写法决定时间的数量级: 用注释的这种写法:耗时2700+ms; 用没注释的这种写法,if括号里写成fabs(h - ano.h) < 1e-8,耗时1200+ms; 现在这种写法:耗时800+ms;; */ bool operator < (const Foo ano) const { // if(fabs(f - ano.f) < 1e-8)return h > ano.h; // else return f > ano.f; if(h == ano.h)return g > ano.g; else return h > ano.h; } } Node; void init0() { facts[0] = facts[1] = 1; for(int i = 2; i < 10; ++i)facts[i] = facts[i - 1] * i; } int encode(string& seq) { int res = 0, len = seq.length(), i, j, cnt; for(i = 0; i < len; ++i) { cnt = (seq[i] - ‘1‘); for(j = 0; j < i; ++j)if(seq[j] < seq[i])--cnt; res += cnt * facts[len - i - 1]; } return res; } string decode(int code) { int i, j, r, t, board = 0; string res; for(t = 9; t > 0; --t) { r = code / facts[t - 1]; code %= facts[t - 1]; for(i = 1; i <= 9; ++i) { if(!((board >> i) & 1)) { if(r == 0)break; else --r; } } res.push_back(i + ‘0‘); board |= 1 << i; } return res; } double distance(int src) { double res = 0.0; string seq = decode(src); int len = seq.length(); int i, j, x0, y0, x1, y1, num; for(i = 0; i < len; ++i) { num = seq[i] - ‘0‘; x0 = (num - 1) / 3, y0 = (num - 1) % 3; x1 = i / 3, y1 = i % 3; res += abs(x0 - x1) + abs(y0 - y1); } return res; } int find_pos9(string& str) { int len = str.length(), i; for(i = 0; i < len; ++i)if(str[i] == ‘9‘)return i; return -1; } const int dirs[] = { -1, 1, -3, 3}; priority_queue<Node> que; bool vis[362888]; pair<int, char> pre[362888]; string ans; bool bfs(int s) { int i, j, k; int pos9, npos9, ncode; string seq; Node top_node; while(!que.empty())que.pop(); memset(vis, 0, sizeof(vis)); for(i = 0; i < facts[9]; ++i)pre[i].first = -1; vis[s] = true; seq = decode(s); pos9 = find_pos9(seq); que.push(Node(s, pos9, 0, distance(s))); while(!que.empty()) { top_node = que.top(); que.pop(); if(top_node.code == 0) { ans.clear(); for(int now = top_node.code, last = pre[top_node.code].first; last != -1; now = last, last = pre[last].first) { ans.push_back(pre[now].second); } reverse(ans.begin(), ans.end()); return true; } seq = decode(top_node.code); pos9 = top_node.pos9; for(i = 0; i < 4; ++i) { npos9 = pos9 + dirs[i]; if(npos9 < 0 || npos9 > 8)continue; if((i == 0 || i == 1) && npos9 / 3 != pos9 / 3)continue; if((i == 2 || i == 3) && (npos9 < 0 || npos9 > 8))continue; swap(seq[npos9], seq[pos9]); ncode = encode(seq); if(!vis[ncode]) { pre[ncode].first = top_node.code; if(i == 0)pre[ncode].second = ‘l‘; else if(i == 1)pre[ncode].second = ‘r‘; else if(i == 2)pre[ncode].second = ‘u‘; else if(i == 3)pre[ncode].second = ‘d‘; que.push(Node(ncode, npos9, top_node.g + 1, distance(ncode))); vis[ncode] = true; } swap(seq[npos9], seq[pos9]); } } return false; } int get_code(char str[]) { string temp; int len = strlen(str), i, j, cnt; for(i = 0; i < len; ++i) { if(isdigit(str[i]))temp.push_back(str[i]); else if(str[i] == ‘x‘)temp.push_back(‘9‘); } cnt = 0; len = temp.length(); for(i = 0; i < len; ++i) { if(temp[i] == ‘9‘)continue; for(j = 0; j < i; ++j) { if(temp[j] == ‘9‘)continue; if(temp[j] > temp[i])++cnt; } } if(cnt & 1)return -1; else return encode(temp); } int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); #endif // ONLINE_JUDGE ios::sync_with_stdio(false); init0(); while(gets(input) != NULL) { int code = get_code(input); if(code < 0)cout << "unsolvable" << endl; else if(code == 0)cout << endl; else cout << (bfs(code) ? ans : "unsolvable") << endl; } return 0; }
3、暑假时候写的A*算法。耗时:400+ms。不知为何怎么这么快?Q_Q
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<queue> #include<string> #include<stack> using namespace std; typedef struct T { int nums[10];//使用1-9 int ctv;//cantor value int index;//空格所在位置。 double g, h; bool operator < (const T ano) const { if(h == ano.h)return g > ano.g; else return h > ano.h; } } Node; Node stnd; const int facts[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; /*r,u,l,d*/ const int dirs[] = {1, -3, -1, 3}; const int N = 9; const int dest_ctv = 1; priority_queue<Node> que; int vis[362888], pre[362888]; int en_cantor(int a[]); void parse2int(char str[]); bool has_solution(); double manhadun(Node n); bool check(int index1, int index2); void debug(Node n); void bfs() { Node n2; while(!que.empty())que.pop(); memset(vis, -1, sizeof(vis)); memset(pre, -1, sizeof(pre)); que.push(stnd); vis[stnd.ctv] = -2; while(!que.empty()) { Node n = que.top(); que.pop(); for(int i = 0; i < 4; i++) { n2 = n; n2.index += dirs[i]; if(!check(n2.index, n.index))continue; swap(n2.nums[n2.index], n2.nums[n.index]); n2.ctv = en_cantor(n2.nums); if(vis[n2.ctv] == -1) { n2.g++; n2.h = manhadun(n2); que.push(n2); vis[n2.ctv] = i; pre[n2.ctv] = n.ctv; } if(n2.ctv == dest_ctv)return; } } } void print() { stack<char> stk; for(int cur = dest_ctv; pre[cur] != -1; cur = pre[cur]) { if(vis[cur] == 0)stk.push(‘r‘); else if(vis[cur] == 1)stk.push(‘u‘); else if(vis[cur] == 2)stk.push(‘l‘); else if(vis[cur] == 3)stk.push(‘d‘); } while(!stk.empty()) { putchar(stk.top()); stk.pop(); } printf("\n"); } int main() { //freopen("probDTDS.txt", "r", stdin); char input_data[40]; while(gets(input_data) != NULL) { parse2int(input_data); if(!has_solution()) { printf("unsolvable\n"); continue; } else { bfs(); print(); } } return 0; } int en_cantor(int a[]) { int res = 0, cnt = 0; const int n = N; for(int i = 1; i <= n; i++) { cnt = 0; for(int j = i + 1; j <= n; j++) { if(a[j] < a[i])cnt++; } res += cnt * facts[n - i]; } return res + 1; } void parse2int(char str[]) { int len = strlen(str); int idx = 1; for(int i = 0; i < len; i++) { if(isdigit(str[i]))stnd.nums[idx++] = str[i] - ‘0‘; else if(str[i] == ‘x‘) { stnd.nums[idx] = N; stnd.index = idx; idx++; } } stnd.ctv = en_cantor(stnd.nums); } bool has_solution() { int re_order = 0; for(int i = 1; i <= N; i++) { for(int j = 1; j < i; j++) { if(stnd.nums[i] != 9 && stnd.nums[j] != 9 && stnd.nums[i] < stnd.nums[j])re_order++; } } return re_order % 2 == 0; } double manhadun(Node n) { double res = 0; for(int i = 1; i <= N; i++) { if(n.nums[i] == 9)continue; int x = (n.nums[i] - 1) / 3, y = (n.nums[i] - 1) % 3; res += fabs((double)(x - (i - 1) / 3)) + fabs((double)(y - (i - 1) % 3)); } return res; } bool check(int index1, int index2) { int mn = min(index1, index2); int mx = max(index1, index2); if(mn < 1 || mn > N || mx < 1 || mx > N)return false; if(mn == 1)return mx == 2 || mx == 4; else if(mn == 2)return mx == 3 || mx == 5; else if(mn == 3)return mx == 6; else if(mn == 4)return mx == 5 || mx == 7; else if(mn == 5)return mx == 6 || mx == 8; else if(mn == 6)return mx == 9; else if(mn == 7)return mx == 8; else if(mn == 8)return mx == 9; else return false; } void debug(Node n) { for(int i = 1; i <= N; i++)printf("%d ", n.nums[i]); printf("\n"); }
以上是关于八数码问题,逆序数判定是不是有解,需要考虑0吗?的主要内容,如果未能解决你的问题,请参考以下文章