defZeroOnePackBasic(N, V, C, W): Vs = V + 1 Ns = N + 1 dp = [[0] * (Vs) for _ inrange(Ns)] for i inrange(1, N+1): for v inrange(C[i-1], Vs): dp[i][v] = max(dp[i-1][v], dp[i-1][v-C[i-1]]+W[i-1]) return dp[N][V]
defZeroOnePackOpt(N, V, C, W): Vs = V + 1 Ns = N + 1 dp = [0] * Vs for i inrange(1, Ns): for v inrange(Vs-1, C[i-1]-1, -1): dp[v] = max(dp[v], dp[v-C[i-1]]+W[i-1]) return dp[V]
defZeroOnePackFull(N, V, C, W): Vs = V + 1 Ns = N + 1 dp = [float('-inf')] * Vs dp[0] = 0 for i inrange(1, Ns): for v inrange(Vs-1, C[i-1]-1, -1): dp[v] = max(dp[v], dp[v-C[i-1]]+W[i-1]) return dp[V]
defCompletePackBasic(N, V, C, W): Vs = V + 1 Ns = N + 1 dp = [0] * Vs for i inrange(1, Ns): for v inrange(Vs-1, C[i-1]-1, -1): tmp = [] for k inrange(0, (v // C[i-1])+1): tmp.append(dp[v-k*C[i-1]]+k*W[i-1]) dp[v] = max(tmp) return dp[V]
优化
优化1 对数据进行预处理:
首先将费用大于 V 的物品去掉,然后使用类似计数排序的做法,计算出费用相同的物品中价值最高的是哪个,可以差不多用O(V+N)地完成这个优化,除非重复费用的物品过多。
defCompletePackVN(N, V, C, W): Vs = V + 1 Ns = N + 1 dp = [0] * Vs for i inrange(1, Ns): for v inrange(C[i-1], Vs): dp[v] = max(dp[v], dp[v-C[i-1]]+W[i-1]) return dp[V]
defMultiPackBasic(N, V, C, W, M): ''' 将多重背包问题简单转换成01背包问题 ''' for i inrange(N): m = M[i] - 1 C += [C[i]] * m W += [W[i]] * m N += m Vs = V + 1 Ns = N + 1 dp = [0] * Vs for i inrange(Ns): for v inrange(Vs-1, C[i-1]-1, -1): dp[v] = max(dp[v], dp[v-C[i-1]]+W[i-1]) return dp[-1]
defMultiPackBinary(N, V, C, W, M): ''' 将多重背包问题利用二进制的特性转换成01背包问题 ''' for i inrange(N): Mi = M[i] k = 2 while k <= Mi: N += 1 W += [W[i] * k] C += [C[i] * k] k *= 2 Vs = V + 1 Ns = N + 1 dp = [0] * Vs for i inrange(1, Ns): for v inrange(Vs-1, C[i-1]-1, -1): dp[v] = max(dp[v], dp[v-C[i-1]]+W[i-1]) return dp[-1]
Proposition: For any positive integer m≤2n−1,there exists a set S⊆{0,1,2,...,n−1} and S=ϕ such that m=∑i∈S2i.
Proof:
It is obvious to get there are totally 2n−1 kinds of S, because the number of permutations is
i=1∑n(in)=2n−1
Also, we can find the maximum of m is 2n−1 and the minimum is 1. There are totally 2n−1, and the range of the sum is from 1 to 2n−1. It means that as long as we prove the sum of each S is unique, we can prove the proposition. In mathematical words, for any Si,Sj where i=j, ∑k∈Si2k=∑k∈Sj2k.
Given two different sets Si and Sj, we can get Gi=Si−Sj and Gj=Sj−Si. WLOG, we only need to prove that ∑k∈Gi2k=∑k∈Gj2k, for any Gi,Gj where i=j.
First situation, either Gi or Gj contains the index 0. WLOG, we assume Gi contains the index 0. Then the component sum of Gi is odd, while the component of Gj is even. Therefore, they must be different.
k∈Gi∑2k=1+k=0,k∈Gi∑2k=k∈Gj∑2k
Second situation, neither Gi or Gj contains the index 0. We defined that Gi={2ik∣ik<ik+1,k=1,2,...,p} where p is the number of entries in Gi, similarly, Gj={2jk∣jk<jk+1,k=1,2,...,q} where q is the number of entries in Gj. WLOG, we set i1<j1.
We assume that the internal summations of Gi and Gj are equal, we can get the following equation:
Since Gi∩Gj=ϕ and neither Gi or Gj contains the index of 0, therefore the left-hand side of equation is odd and the right-hand side is even, which contradicts the previous assumption of equivalence of internal summation of Gi and Gj.
Thus, we can prove that the summation of any S is unique, which further proves one-to-one mapping relation that for any positive integer m≤2n−1,there exists a unique set S⊆{0,1,2,...,n−1} and S=ϕ such that m=∑i∈S2i.
defcanFullPack(N, V, C, M): Ns = N + 1 Vs = V + 1 dp = [-1] * Vs dp[0] = 0 for i inrange(1, Ns): for v inrange(V): if dp[v] >= 0: dp[v] = M[i-1] elif dp[v] == -1: dp[v] = -1 for v inrange(C[i-1], Vs): dp[v] = max(dp[v-C[i-1]]-1, dp[v]) return dp[-1] >= 0