CurValue + rest <= BestValue
# 递归方式
class pack_01_back_prune_test:
def __init__(self,N,V,C,W):
self.num =N
self.V = V
self.C = C
self.W = W
self.BestResult = [False]*N
self.Selected = [False]*N
self.BestValue = 0
self.CurCost = 0
self.CurValue = 0
# bound()限界函数
self.rest = 0
for i in range(N):
self.rest += W[i]
def pack_01_back_tracking(self,depth):
if depth > self.num-1:
if self.CurValue > self.BestValue:
self.BestValue = self.CurValue
self.BestResult[:] = self.Selected[:]
else:
# 满足约束条件和限界函数的处理
if self.CurCost + self.C[depth] <= self.V and self.CurValue + self.rest > self.BestValue:
self.Selected[depth] = True
self.CurCost += self.C[depth]
self.CurValue += self.W[depth]
self.rest -= self.W[depth]
# next
self.pack_01_back_tracking(depth+1)
# undo
self.CurCost -= self.C[depth]
self.CurValue -= self.W[depth]
self.rest += self.W[depth]
# 满足限界函数
if self.CurValue + self.rest > self.BestValue:
self.Selected[depth] = False
self.pack_01_back_tracking(depth+1)
def print_Result(self):
self.pack_01_back_tracking(0)
print(self.BestResult)
print(self.BestValue)
# 迭代方式
#%%
# 这种解法注意回溯函数一致性,思路比较清晰:满足剪枝条件就回溯:CurValue + rest <= BestValue
# 这是基于深度优先搜索的回溯法剪枝,以下是迭代实现方法
def pack_01_back_prune_iteration_test(N,V,C,W):
depth = 0
BestResult = [False]*N
Selected = [False]*(N)
BestValue = 0
CurCost = 0
CurValue = 0
# bound()限界函数
rest = 0
for i in range(N):
rest += W[i]
while True:
# 尽量向左走直到不满足约束条件
while depth < N and CurCost + C[depth] <= V:
rest -=W[depth]
Selected[depth] = True
CurCost += C[depth]
CurValue += W[depth]
depth +=1
# 走到底,结果处理
if depth >= N:
BestValue = CurValue
BestResult[:] = Selected[:]
# 不能往左走,就向右走,注意这里只是走一步而已
else:
rest -=W[depth]
Selected[depth] =False
depth +=1
# 当不满足限界函数的时候,就需要回溯,注意底部也满足这个条件
while CurValue + rest <= BestValue:
# 回溯的处理,之所有需要depth -=1,上面走的时候都depth++了,底部也是这样
depth -=1
while depth >=0 and not Selected[depth]:
rest +=W[depth]
depth -=1
# 当回溯到root的之后,无法回溯了,输出结果
if depth < 0:
return BestResult,BestValue
# 回溯恢复现场
else:
Selected[depth] =False
CurCost -= C[depth]
CurValue -= W[depth]
depth +=1
# 运行
N = 8
V = 30
C = [11,2,3,9,13,6,15,7,19]
W = [5.0,2.0,5.0,7.0,5.0,11.0,6.0,14.0]
print(pack_01_back_prune_iteration_test(N,V,C,W))
pack_01_back_prune_test(N,V,C,W).print_Result()
([False, True, True, True, False, True, False, True], 39.0)
[False, True, True, True, False, True, False, True]
39.0
也使用一样的限界函数:
CurValue + rest <= BestValue
回溯法深度优先很快就能得到一个可行解,进而可以更新BestValue,分支限界法宽度优先,可行解都在最后一层,BestValue一直得不到更新,那就是限界函数一直不起作用
为了使限界函数早生效,我们应该提前更新BestValue,在满足约束条件下,进入左子树就可以更新BestValue,因为最终的BestValue是满足约束条件下Curvalue里面的最大值
#%%
class FIFO_01_Pack_prune:
def __init__(self,N,V,C,W):
self.num =N
self.Volume = V
self.Cost = C
self.Value = W
self.BestValue = 0
#用于存放活结点,便于理解,把根结点,以及第0层结束标志-1放进去
# 结点包括2个属性:当前空间大小,当前的价值大小
self.queue = [[0,0],[-1,-1],]
# 当前剩余价值和,bound()限界函数
self.rest = 0
# 把第一个减去,因为我们要在进入这一层前更新rest
for i in range(1,N):
self.rest += W[i]
# 实现时叶子结点不加入到活结点列表
def enQueen(self,pair,depth):
if depth < self.num -1:
self.queue.append(pair)
def pack_01(self):
# selected = [0]*self.num
# 首先取出根结点
depth = 0
pair = self.queue.pop(0)
CurCost = pair[0]
CurValue = pair[1]
while True:
# 判断左结点能否加入到队列,能的话,把当前空间和当前价值放入队列,满足约束条件
if CurCost + self.Cost[depth] < self.Volume:
# 满足限界函数
if CurValue + self.Value[depth] + self.rest > self.BestValue:
# 在进入左子树时,更新bestvalue
self.BestValue = CurValue + self.Value[depth]
self.enQueen([CurCost + self.Cost[depth],CurValue + self.Value[depth]],depth)
# 右满足限界函数
if CurValue + self.Value[depth] + self.rest > self.BestValue:
self.enQueen([CurCost,CurValue],depth)
# 然后弹出下一个结点
pair = self.queue.pop(0)
CurCost = pair[0]
CurValue = pair[1]
# 当同一层处理完毕时,先判断是否能够输出结果,判断的标准是队列是否为空,
# 这时下一层的所有结点已经加入了队列,这时需要把下一层
# 增加一个结尾-1便于判断,然后进入下一层,弹出下一个结点
if CurCost == -1:
if not self.queue:
return self.BestValue
self.enQueen([-1,-1],depth)
depth += 1
# 在刚进入下一层时,更新rest
self.rest -= self.Value[depth]
# 弹出下一个结点
pair = self.queue.pop(0)
CurCost = pair[0]
CurValue = pair[1]
def print_Result(self):
print(self.pack_01())
#%%
N = 8
V = 30
C = [11,2,3,9,13,6,15,7,19]
W = [5.0,2.0,5.0,7.0,5.0,11.0,6.0,14.0]
#pack_01_back_test(N,V,C,W).print_Result()
FIFO_01_Pack_prune(N,V,C,W).print_Result()