迭代加深搜索算法总结 + 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
", 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
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Bowen-/p/4952431.html