回溯法:求出解空间中满足约束条件的所有解。
分支界限法:找出满足约束条件的一个解。
搜索方式:回溯法使用深度优先的方式,而分支界限法使用广度优先或最小耗费的方式搜索解空间。
1. 单源最短路径问题:
问题描述:在有向图中,求出源顶点 s 到 目的顶点的最短路径。
解决办法:使用优先队列式的分支界定法。
算法描述:
1. 使用最小堆MinHeap来存储活结点列表,初始时MinHeap={ Node }, Node.indx = s, Node.len = 0.
2. 使用 dist[1...MaxNode] 来存储s到最各结点的最小距离,初始时 dist[1...MaxNode] = Inf, 但是 dist[s] = 0.
3. 循环以下操作直到 MinHeap为空:
取MinHeap堆顶元素E.
针对 i = 1 ... MaxNode:
若 adj[ E.indx ][ i ] < Inf && E.len + adj[ E.indx ][ i ] < dist[ i ], 则: // [这里是剪枝的条件]
dist[ i ] = E.length + adj[ E.indx ][ j ]
pre[ i ] = E.indx
Node={"indx":i, "len":dist[i]}, 将Node 加入堆MinHeap中, 即扩展堆顶的结点。
python 代码如下:
2. 0-1背包:
( 0-1背包的分枝界定算法法类似于A*算法。)
输入:N个物品重量、价值。 背包重量: W.
算法步骤:
1. 计算出N个物品的单位价值,按单位价值排序。设排序后N个物品的重量: w1, w2, ..., wN;N个物品的价值:v1, v2, ..., vN。
2. 设初始重量w=0, 初始价值v=0.
3. 初始化最大堆的结点:{w=0,v=0, candidate_indx = 1, upbenefit = v + (W-w)*(v1/w1)}。
4. 对于最大堆的堆顶( upbenefit 最大者在堆顶 )进行扩展:
设最大堆顶为:{w_cur, v_cur, candidate_indx, upbenefit }。
若:
a. candidate_indx == N+1, 即堆顶为解空间树的叶子节点且目标函数值upbenefit最大,即求得最优解。
b. 若 w[ candidate_indx ] > W - w_cur, 即候选节点重量超出背包剩下容量, 放弃此堆顶。
c. 若 w [ candidate_indx ] <= W - w_cur, 扩展此候选节点:
删除堆顶,将堆顶进行扩展 并 将扩展后的两个节点加入最大堆( upbenefit )中。
1) 将candidate_indx 节点加入背包中:
在堆中加一个节点:{ w_cur + w[ candidate_indx ], v_cur + v[ candidate_indx ], candidate_indx + 1, upbenefit = w_cur + w[ candidate_indx ] + (W-w_cur-w[candidate_indx]) * ( v[candidate_indx + 1] / w[candidate_indx] ) }
即:将当前候选节点加入背包中,并计算背包的重量、价值、及 可能的最大值。
2) 不加 candidate_indx 节点:
在堆中加一个节点:{ w, v, candidate_indx+1, v + (W-w)*( v[candidate_indx + 1] / w[candidate_indx] ) }
上述算法中,upbenefit 为当前选择下,系统可能的最大价值。即已经选择物品的价值v + 未选择物品中单位价值最大值 x 剩余空间。
堆中每个节点代表了一种背包方案。
示例:
假设有4个物品,其重量分别为(4, 7, 5, 3),价值分别为(40, 42, 25, 12),背包容量W=10。
其执行过程如下图所示:
上面的解空间树中:
w代表背包当前重量,v表示背包当前价值,ub代表在该节点代表的方案下背包最大可能价值。
python代码如下: