Skip to content

数据结构之搜索算法

📅 发表于 2017/12/31
🔄 更新于 2017/12/31
👁️ 次访问
📝 0 字
0 分钟
算法
#搜索

搜索算法、装载问题

搜索算法

解空间树

子集树

比如n种可选物品的01背包,对其进行选择。解空间为长度为n的01向量表示。解空间树是一颗完全二叉树。

排列树

比如旅行商问题,对n进行排列。

解空间搜索方式

DFS

深度优先。回溯法

BFS

广度优先或最小耗费优先。分支限界法

在扩展节点处,先生成所有的儿子节点,再从活节点列表中选择一个最有利的节点作为新的扩展节点。

回溯法

就是穷举深度优先去找到解空间中满足约束条件的所有解,再选择一个最好的出来。

类似于走迷宫:走到死路或者已经达不到最优解就及时回头。要注意剪枝

搜索子集树

c++
void dfs(int t) {
  if (t > n) {
    output(x);
    return;
  }
  // 0和1两种选择
  for (int i = 0; i <= 1; i++) {
    x[t] = i;
    if (legal(t)) {
      bfs(t+1);
    }
  }
}

搜索排列树

c++
void dfs(int t) {
  if (t > n) {
    output(x);
    return;
  }
  // 遍历所有t的可能:还剩下的可选择的内容,前面t-1已经确定了
  for (int i = t; i <= n; i++) {
    swap(x[t], x[i]);
    if (legal(t)) {
      bfs(t + 1);
    }
    swap(x[t], x[i]);
  }
}

分支限界法

广度优先或者最小消费优先去搜索解空间树,去找到使目标函数达到极大或者极小解,某种意义下的最优解。

每个活节点只有一次机会成为扩展节点,成为时,就一次性产生其所有儿子节点。舍弃不可行或导致非最优解的儿子节点,其余儿子节点加入活节点列表。从活节点列表中取出下一个节点作为新的扩展节点。

广度优先搜索

队列式分支限界法

最小消费优先搜索

优先队列式分支限界法。每个活节点有一个优先级。通常用最大堆来实现最大优先队列。

装载问题

问题描述

n个集装箱质量分别为wi, 要装上总容量为c的轮船。 问,怎样装,才能使装得最多?

maxinxiwiinxiwicxi{0,1},表示装或不装

贪心解法

贪心策略:集装箱从轻到重排序,轻者先装, 直到超重。

BFS

有2艘轮船,inwic1+c2, 怎样才能把n个集装箱装到这2艘轮船。

可知最优方案:尽量把第一艘轮船装满,剩余的装到第二艘上。

定义Solution

c++
class Solution {
    public:
        // 集装箱数量
        int n;
        // 集装箱的重量
        vector<int> w;
        // 船的载重
        int c;
        // 当前重量
        int cw;
        // 最优重量
        int bestw;
		// 回溯遍历index=i的箱子
        void backtrack(int i);
		
  		// 构造函数
        Solution(const vector<int> &w, int c):w(w), c(c) {
            this->cw = 0;
            this->bestw = 0;
            this->n = w.size();
        }
};

int max_loading(const vector<int> &w, int c) {
    Solution solu(w, c);
    solu.backtrack(0);
    return solu.bestw;
}

无剪枝的dfs

c++
void Solution::backtrack(int i) {
    if (i == n) {
        if (cw > bestw) {
            bestw = cw;
        }
        return;
    }
	// 选择i
    if (cw + w[i] <= c) {
        cw = cw + w[i];
        backtrack(i + 1);
        cw = cw - w[i];
    }
    // 不选择i
    backtrack(i + 1);
}

剪枝的dfs

进入下一步遍历之前,检查cw+rest>bestw ,如果剩下的所有加上都小于最优解的话,那么就不用进入了。

c++
/*
 * 剪枝的dfs
 */
void Solution::dfs(int i) {
    if (i == n) {
        if (cw > bestw) {
            bestw = cw;
        }
        return;
    }
    // 剩余
    r = r - w[i];
    // 选择i
    if (cw + w[i] <= c) {
        cw += w[i];
        // 剪枝
        if (cw + r > bestw) {
            dfs(i + 1);
        }
        cw -= w[i];
    }

    // 不选择i,剪枝
    if (cw + r > bestw) {
        dfs(i + 1);
    }
    r = r + w[i];
}
总访客数:   ·   总访问量:
PLM's Blog @ 2016 - 2025