1. 程式人生 > >揹包問題求第K優解

揹包問題求第K優解

以01揹包為例:

首先看01揹包求最優解的狀態轉移方程: F [i, v] = max {F [i − 1, v], F [i −1, v − C i ] + W i } 。如果要求第 K 優解,那麼狀態 F [i, v] 就應該是一個大小為 K 的佇列 F [i, v, 1 . . . K] 。其中 F [i, v, k] 表示前 i 個物品中,揹包大小為 v 時,第 k 優解的值。這裡也可以簡單地理解為在原來的方程中加了一維來表示結果的優先次序。顯然 f [i, v, 1 . . . K] 這 K 個數是由大到小排列的,所以它可看作是一個有序佇列。

為什麼這個方法正確呢?實際上,一個正確的狀態轉移方程的求解過程遍歷了所有可用的策略,也就覆蓋了問題的所有方案。只不過由於是求最優解,所以其它在任何一個策略上達不到最優的方案都被忽略了。如果把每個狀態表示成一個大小為 K 的陣列,並在這個陣列中有序地儲存該狀態可取到的前 K 個最優值。那麼,對於任兩個狀態的max運算等價於兩個由大到小的有序佇列的合併。

實現如下

import numpy as np
def pack_01_Bottom_up(N,V,C,W,K):           
    list = np.zeros((K,V+1),dtype = int)
    A =[0]*(K+1)
    B =[0]*(K+1)
    for i in range(1,N+1):
        for v in range(V,C[i-1]-1,-1):
            for k in range(K):
                A[k] = list[k,v]
                B[k] = list
[k,v-C[i-1]] + W[i-1] A[K],B[K] = -1,-1 x,y,k =0,0,0 # 因為去重,所以x<K,y<K就無法保證k取到K了,所以用到了一個trick,這裡是歸併排序歸併操作的改進 while k < K and (A[k] !=-1 or B[k] !=-1): if A[x] > B[y]: list[k,v]= A[x] x +=
1 else: list[k,v] = B[y] y +=1 # 去重 if list[k,v] != list[k-1,v]: k +=1 return list[:,V]