迭代加深搜索算法总结 + Editing a Book UVa11212题解
迭代加深搜索算法:
对于可以用回溯法解决,但是解答树结点数大的恐怖的问题的一种解决办法,有的问题甚至用bfs连一层节点都遍历不完就超时了。具体方法就是依次枚举搜索层数,从1到一个上限。
结构:
int solve() {
for (int maxd = 1; maxd < MAXN; maxd++) {
if (dfs(0, maxd)) return maxd;
}
return MAXN;
}
优点:
相较于bfs老老实实的枚举解答树而言, 迭代加深搜索算法可以以深度优先搜索,也就是说可以第一时间到达最深层次进行搜索。平均时间上而言比bfs逐层搜索要快太多太多了,再配合回溯剪枝。优化效果相当可观!
举例:
UVa11212 Editing a Book
对于这道题, 0 < n < 10, 也就是总状态有9! = 362880个。 虽然看起来很少,但是每个结点可以扩展到的结点个数是 (n * (n/2) * (n/2)) = (n^3)/4 ≈ 183, 再加上对每个结点判重和剪枝计算,每个结点的计算量大概是 183 * 40 = 7290
所以对于一组n = 9的数据,最差情况的计算量是 n! * (n^3)/4 * 40 ≈ 26亿
然而测试数据一共有20组
也就是说 总计算量是 26亿 * 20 = 520亿
即便我用bfs+剪枝, 程序还是毫无悬念的超时了。
然而如果我用迭代加深搜索算法,20组数据运行时间总和是49ms, 快了2000倍!
UVa11212 Editing a Book 迭代加深搜索+剪枝(49ms, Ac)
#include <cstdio> #include <cstring> using namespace std; int n, idx, maxd, a[9]; bool ok; void create(int start, int len, int k) { int olda[9]; memcpy(olda, a, sizeof(a)); int z = 0, i = 0; for (; z < k; z++) { if (z >= start && z < start + len) { z += (len - 1); continue;} a[i++] = olda[z]; } for (z = 0; z < len; z++) a[i++] = olda[start + z]; for (z = k; z < n; z++) { if (z >= start && z < start + len) { z += (len - 1); continue;} a[i++] = olda[z]; } } int num_unordered() { int cnt = 0; for (int i = 1; i < n; i++) if (a[i - 1] + 1 != a[i]) cnt++; cnt = cnt ? cnt + 1 : 0; return cnt; } bool success() { for (int i = 0; i < n - 1; i++) if (a[i] + 1 != a[i + 1]) return false; return true; } bool dfs(int d) { if (d * 3 + num_unordered() > 3 * maxd) return false; if (success()) { ok = 1; return true;} int temp[9]; memcpy(temp, a, sizeof(a)); for (int i = 0; i < n; i++) for (int j = i + 1; j <= n; j++) for (int k = 0; k <= n; k++) { if (k >= i && k <= j) { k = j; continue;} create(i, j - i, k); if (dfs(d + 1)) return true; memcpy(a, temp, sizeof(a)); } return false; } int solve() { if (success()) return 0; ok = 0; for (maxd = 1; maxd < 5; maxd++) if (dfs(0)) return maxd; return maxd; } int main() { int T = 0; while (scanf("%d", &n) == 1 && n) { printf("Case %d: ", ++T); for (int i = 0; i < n; i++) scanf("%d", &a[i]); int ans = solve(); printf("%d\n", ans); } return 0; }
UVa11212 Editing a Book BFS搜索+剪枝 (估计耗时18s)
#include <cstdio> #include <cstdlib> #include <queue> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 400000, MAXHASHSIZE = 100003; int n, idx, target; int a[10], _head[MAXHASHSIZE], _next[MAXN], st[MAXN]; struct Node { int arr[10], dis, num; Node (int d = 0): dis(d) {} void trans() { num = 0; for (int i = 0; i < n; i++) { num *= 10; num += arr[i]; } } }; bool try_to_insert(Node &cur) { int h = cur.num % MAXHASHSIZE; int u = _head[h]; while (u) { if (st[u] == cur.num) return false; u = _next[u]; } st[idx] = cur.num; _next[idx] = _head[h]; _head[h] = idx++; return true; } void create(Node &from, Node &des, int start, int len, int k) { int z = 0, i = 0; for (; z < k; z++) { if (z >= start && z < start + len) { z += (len - 1); continue;} des.arr[i++] = from.arr[z]; } for (z = 0; z < len; z++) des.arr[i++] = from.arr[start + z]; for (z = k; z < n; z++) { if (z >= start && z < start + len) { z += (len - 1); continue;} des.arr[i++] = from.arr[z]; } } bool impossible(Node &cur) { int cnt = 0; for (int i = 1; i < n; i++) if (cur.arr[i - 1] + 1 != cur.arr[i]) cnt++; cnt = cnt ? cnt + 1 : 0; return cnt > 3 * (5 - cur.dis); } int bfs() { queue<Node> q; Node start; for (int i = 0; i < n; i++) start.arr[i] = a[i]; start.trans(); try_to_insert(start); q.push(start); while (!q.empty()) { Node cur = q.front(); q.pop(); cur.trans(); if (cur.num == target) return cur.dis; for (int i = 0; i < n; i++) for (int j = i + 1; j <= n; j++) for (int k = 0; k <= n; k++) { if (k >= i && k <= j) { k = j; continue;} Node nextn; create(cur, nextn, i, j - i, k); nextn.trans(); if (!impossible(nextn) && try_to_insert(nextn)) { nextn.dis = cur.dis + 1; q.push(nextn); } } } return 0; } int main() { int T = 0; while (scanf("%d", &n) == 1 && n) { printf("Case %d: ", ++T); target = idx = 0; for (int i = 0; i < n; i++) { scanf("%d", &a[i]); target *= 10; target += i + 1; } memset(_head, 0, sizeof(_head)); int ans = bfs(); printf("%d\n", ans); } return 0; }