CPH-第五章 搜索

  • Complete search(穷举搜索),也称为暴力搜索或枚举。

  • 核心思想:当问题规模允许时,系统性地遍历所有可能的候选解,是解决问题最直接、最可靠的方法。

5.1 子集生成(Generating Subsets)

  • 生成一个包含 \(n\) 个元素的集合的所有子集,是穷举搜索的基础问题。一个包含 \(n\) 个元素的集合共有 \(2^n\) 个子集。

方法一:递归`

  • 使用递归优雅地遍历所有子集。对于每个元素,都有“选取”和“不选取”两种选择,递归函数沿着这个决策树展开,最终生成所有组合。
void search(int k) {
  if (k == n) {
    // process subset
  } else {
    search(k+1);
    subset.push_back(k);
    search(k+1);
    subset.pop_back();
  }
}

方法二:利用整数的位表示

  • 每个子集都可以用一个 \(n\) 位的二进制数表示,其中第 \(i\) 位为 1 代表选取第 \(i\) 个元素,为 0 则代表不选取。通过遍历从 \(0\) 到 \(2^n - 1\) 的所有整数,就能系统地生成所有子集。
for (int b = 0; b < (1 << n); b++) {
  vector<int> subset;
  for (int i = 0; i < n; i++) {
    if (b & (1 << i)) subset.push_back(i);
  }
}

5.2 排列生成(Generating Permutations)

  • 与子集不同,排列关心元素的顺序。一个包含 \(n\) 个元素的集合共有 \(n!\) 种排列。

    • 函数 search() 通过逐步为每个位置选择尚未使用的元素,来构建排列。
  • 由于阶乘增长极快,排列生成通常只适用于 (n) 较小的情况。

5.3 回溯(Backtracking)

  • 回溯是一种有组织的穷举搜索,它通过递归系统地构建候选解。其过程可以看作是对一棵搜索树进行深度优先遍历,当发现当前路径无法导向有效解时,就“回溯”到上一步,尝试其他分支。

  • 回溯的核心优势在于其通用性。许多经典问题,如八皇后问题、数独求解、图着色等,都可以用回溯框架清晰地表达和解决。

  • 纯粹的穷举搜索可能会探索大量无效的分支。剪枝是一种优化技术,它通过提前判断某些分支不可能产生有效解,从而直接跳过对这些分支的探索,显著减少搜索空间。

  • 一个有效的剪枝策略需要满足两个条件:

    • 正确性:剪枝判断必须保证不会误删包含有效解的分支。
    • 高效性:剪枝判断本身的开销,应当远小于它所能节省的搜索开销。

5.5 折半查找(Meet in the Middle)

  • 当问题规模 \(n\) 使得 \(2^n\) 或 \(n!\) 的穷举不可行时(例如 \(n\) 在 40 左右),折半查找是一种强大的优化技术。

  • 它的核心思想是将搜索空间一分为二:

    • 分别生成两半的所有可能状态(例如,各 \(2^{n/2}\) 个子集)。
    • 对其中一半的状态进行排序或存入哈希表。
    • 遍历另一半的状态,并在已处理的一半中高效地查找能与当前状态匹配的解。
  • 这种方法将时间复杂度从 \(O(2^n)\) 显著降低到 \(O(2^{n/2} \log 2^{n/2})\) 或 \(O(2^{n/2})\),使得一些原本无法解决的问题变得可行。典型的应用是子集和问题(Subset Sum)。

5.6 总结:穷举搜索的策略选择

  • 穷举搜索的核心在于根据问题规模和结构,选择合适的完整搜索策略:
策略 核心思想 适用场景/复杂度直觉
子集/排列生成 系统地枚举所有组合或顺序 问题规模 \(n\) 很小 (\(2^n\) 或 \(n!\) 可接受)
回溯 有组织地深度优先搜索解空间 需要满足一系列约束条件的构造性问题
回溯 + 剪枝 在回溯基础上,提前排除无效分支 搜索空间大,但存在有效的可行性判断
折半查找 将搜索空间拆分,利用排序/哈希连接两半 \(n\) 稍大(如 40),\(2^n\) 不可行但 \(2^{n/2}\) 可行
  • 系统枚举思想,是后续学习更高级算法的重要基础。
posted @ 2026-10-28 21:09  LifeLotus  阅读(2)  评论(0)    收藏  举报