限界分支法(实际上没有剪枝,介绍的是广度优先搜索):01揹包问题,队列实现方式(FIFO)

限界分支法:队列实现方式

前面已经介绍过限界分支法大部分是基于广度优先搜索,广度优先搜索一般借助于队列实现,剪枝的情况可以借助于优先级队列。

实现如下:

#%%
class FIFO_01_Pack:
    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],]  

    # 实现时叶子结点不加入到活结点列表,当属于叶子结点时,增加对结果的处理
    def enQueen(self,pair,depth):
        if depth == self.num -1:
            CurValue = pair[1]
            if CurValue > self.BestValue:
                self.BestValue = CurValue
        else:
            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:
                self.enQueen([CurCost + self.Cost[depth],CurValue + self.Value[depth]],depth)
            # 右结点总是可以加入队列的,因为没有约束条件的限制
            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
                pair = self.queue.pop(0)
                CurCost = pair[0]
                CurValue = pair[1]
    
    def print_Result(self):
        print(self.pack_01())

Baseline对比及结果输出:

class pack_01_back_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
    
    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:
                self.Selected[depth] = True
                
                self.CurCost += self.C[depth]
                self.CurValue  += self.W[depth]
                # next
                self.pack_01_back_tracking(depth+1)
                # undo
                self.CurCost -= self.C[depth]
                self.CurValue  -= self.W[depth]

            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)

#%%
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(N,V,C,W).print_Result()


[False, True, True, True, False, True, False, True]
39.0
39.0

追踪解

追踪解,上述实现的情况下,解都在最后一层,根本不知道之前的路径是怎样的,广度优先搜索,同一个纬度,假如不加指标判断的话,根本不知道最优解是选择的哪一个,所以需要同一个纬度的每一个结点,记住他之前的路径,才能在最优解的时候之前是怎么走过来的,每一个结点用一个数组记录路径,这样实现的感觉消耗有点大啊,通常看见是采用链表方式

点赞