迭代加深搜索

迭代加深搜索是一种状态空间搜索策略,其实质是多轮次地执行有深度限制的深度优先搜索,直到找到目标解或确定无解为止。其中随着轮次的增加,搜索深度的上限也不断增加。

记当前搜索的深度限制为 \(\tau\),算法的主要流程如下:

  1. 先将深度限制设为 \(\tau = 1\)
  2. 从起始状态出发,执行深度不超过 \(\tau\) 的深度优先搜索。
    • 如果当前状态是目标解,则算法终止并返回相关信息。
    • 如果当前状态的深度 \(d\) 达到深度限制,或其所有子状态均以访问过,则返回前一状态;否则依次递归地处理其尚未访问过的子状态。
  3. 按照指定的步长,增加搜索深度限制。
  4. 如果全部搜索结束且没有找到目标解,则输出无解。

当找某个搜索问题的最优解(步数最少的目标解)时,往往优先考虑的是 BFS。而某些问题由于状态的存储消耗很大,单纯的 BFS 可能会导致空间消耗过多。而迭代加深搜索可以看作是用 DFS 方式实现的 BFS,它的空间消耗相对更小,在 DFS(可能时间超限)和 BFS(可能空间超限)之间达成了一种平衡。

例题:UVA529 Addition Chains

题目要求寻找最小长度 \(m\),但由于序列长度不确定,且搜索过程可能会非常深,单纯的 BFS 会因状态过多导致空间爆炸,而单纯的 DFS 则可能深陷无效分支。

为了平衡,采用迭代加深搜索。从小到大限制搜索的深度,如果在当前深度限制下找到了目标值 \(n\),则该深度即为最短长度。在寻找下一个数时,从大到小枚举之前的某两个数,因为较大的数能更快逼近目标 \(n\)

还可以引入一些剪枝,比如假设当前序列最后一个数为 \(v\),距离深度限制还差 \(\Delta\) 步。即使每一步都取最大增长(即当前的数翻倍),能达到的最大值也仅为 \(v \times 2^{\Delta}\),如果这个数小于 \(n\),说明在当前限制下绝对无法到达 \(n\),直接回溯。

参考代码
#include <cstdio>
const int N = 10005;
int n, path[N];
/**
 * DFS 搜索函数
 * @param u 当前搜索到的层数(从 0 开始)
 * @param depth 当前限制的总深度(最大下标)
 */
bool dfs(int u, int depth) {
    if (u == depth) return path[u] == n;
    // 剪枝:如果当前值以最快速度(翻倍)增长都无法在规定步数内达到 n
    // path[u] * 2^(depth - u) < n
    int maxv = path[u];
    for (int i = 0; i < depth - u; i++) {
        maxv *= 2;
        if (maxv >= n) break;
    }
    if (maxv < n) return false;
    // 策略:从大到小枚举,优先尝试较大的数,以便更快接近 n
    for (int i = u; i >= 0; i--) {
        for (int j = i; j >= 0; j--) {
            int val = path[i] + path[j];
            // 必须满足严格递增且不大于目标值
            if (val > path[u] && val <= n) {
                path[u + 1] = val;
                if (dfs(u + 1, depth)) return true;
            }
        }
    }
    return false;
}
int main()
{
    while (true) {
        scanf("%d", &n);
        if (n == 0) break;
        if (n == 1) {
            printf("1\n");
            continue;
        }
        path[0] = 1;
        // 迭代加深搜索:增加深度限制 m (代表序列的最大下标)
        int depth = 0;
        while (true) {
            if (dfs(0, depth)) {
                // 输出结果
                for (int i = 0; i <= depth; i++) {
                    printf("%d%s", path[i], i == depth ? "" : " ");
                }
                printf("\n");
                break;
            }
            depth++;
        }
    }
    return 0;
}

习题:P10494 [USACO02FEB] Power Hungry Cows

解题思路

搜索空间很大而答案在一个比较小的深度内,适合迭代加深搜索

  • 状态表示dfs(a, b, step, lim),其中 ab 分别代表两个工作变量当前的指数,step 为当前步数,lim 为当前设定的最大深度限制。
  • 剪枝策略
    1. 如果当前最大的指数 a 经过剩余所有步数(lim - step)连续翻倍(即每次都进行 a + a)仍无法达到目标值 P,则当前路径必然无解。
    2. 计算 ab 的最大公约数 g,如果目标 P 不能被 g 整除,那么无论如何加减都无法得到 P,可以提前返回。
参考代码
#include <cstdio>
#include <algorithm>
using namespace std;
int p;
// 求最大公约数,用于剪枝
int gcd(int x, int y) {
    return y == 0 ? x : gcd(y, x % y);
}
/**
 * 搜索函数
 * @param a 当前变量1的指数值
 * @param b 当前变量2的指数值
 * @param step 当前已进行的步数
 * @param lim 当前迭代加深预设的最大深度限制
 */
bool dfs(int a, int b, int step, int lim) {
    // 成功条件:其中一个变量达到目标指数 p
    if (a == p || b == p) return true;
    // 达到当前搜索深度限制仍未找到解 
    if (step == lim) return false;
    // 始终保持 a 为较大值,简化逻辑
    if (a < b) swap(a, b);
    // 剪枝 1:即便剩下的每一步都让当前最大值翻倍,如果依然无法达到 p,则说明此路径无解
    if ((a << (lim - step)) < p) return false;
    // 剪枝 2:如果 a 和 b 的最大公约数 g 大于 1,且目标 p 不能被 g 整除,
    // 那么仅通过 a, b 的加减运算永远无法得到 p
    int g = gcd(a, b);
    if (g > 1 && p % g != 0) return false;
    // 尝试所有可能的运算组合 (a+a, a+b, b+b, a-b)
    int v[4] = {a + a, a + b, b + b, a - b};
    for (int i = 0; i < 4; i++) {
        int nv = v[i];
        // 排除无效值或范围过大的值
        if (nv <= 0 || nv > 2 * p) continue;
        // 如果生成的新值与现有值重复,跳过
        if (nv == a || nv == b) continue;
        // 递归搜索:将新生成的值 nv 分别替换旧的 a 或 b
        if (dfs(nv, a, step + 1, lim)) return true;
        if (dfs(nv, b, step + 1, lim)) return true;
    }
    return false;
}
int main()
{
    scanf("%d", &p);
    // 特判:目标是 1 次幂,不需要操作
    if (p == 1) {
        printf("0\n");
    } else {
        // 迭代加深:从 1 步开始尝试,直到找到解
        // 这种方法保证了找到的第一个解一定是最少步数
        int lim = 1;
        while (true) {
            // 初始状态:变量1为 x^1 (指数为1),变量2为 x^0 (1, 指数为0)
            if (dfs(1, 0, 0, lim)) {
                printf("%d\n", lim);
                break;
            }
            lim++;
        }
    }
    return 0;
}
posted @ 2026-02-08 10:27  RonChen  阅读(11)  评论(0)    收藏  举报