背包问题求第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 +=1else:list[k,v] = B[y]y +=1# 去重 if list[k,v] != list[k-1,v]:k +=1return list[:,V]總結
- 上一篇: 背包问题追踪解
- 下一篇: 最长公共子序列和追踪解