背包问题 背包问题 (Knapsack Problem )是一类常见的组合优化问题。其问题描述为:给定一个固定大小、能够携重W W W 的背包,以及一组有价值和重量的物品,找出一个最佳解决方案,使得装入背包的物品总重量不超过W W W ,且总价值最大。
通常情况下,背包问题可以分为以下三类:
0-1 背包问题 :每种物品仅有一件,可以选择放或不放。完全背包问题 :每种物品有无限件,可以选择放多少件或不放。多重背包问题 :每种物品有n i n_i n i 件,可以选择放多少件或不放。本文将介绍如何使用 Python 解决以上三类背包问题。
0-1 背包问题 0-1 背包问题 (0-1 Knapsack Problem )是最基础的背包问题。其问题描述为:给定一个固定大小、能够携重W W W 的背包,以及N N N 个价值、重量分别为v i v_i v i 、w i w_i w i 的物品,找出一个最佳解决方案,使得装入背包的物品总重量不超过W W W ,且总价值最大。
例
有一个容量为10 10 10 的背包,现有4 4 4 个物品,其价值和重量分别为:
求背包能装下的最大价值以及取得最大价值时的物品组合。
解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 N = 4 W = 10 v = [0 , 1 , 3 , 5 , 9 ] w = [0 , 2 , 3 , 4 , 7 ] dp = [[0 ] * (W + 1 ) for _ in range (N + 1 )] flag = [ [0 ] * (W + 1 ) for _ in range (N + 1 ) ] for i in range (1 , N + 1 ): for j in range (1 , W + 1 ): if j < w[i]: dp[i][j] = dp[i - 1 ][j] elif dp[i - 1 ][j] > dp[i - 1 ][j - w[i]] + v[i]: dp[i][j] = dp[i - 1 ][j] flag[i][j] = flag[i - 1 ][j] else : dp[i][j] = dp[i - 1 ][j - w[i]] + v[i] flag[i][j] = i - 1 ans = dp[N][W] sol = [0 ] * Nwhile flag[N][W] != 0 : temp = flag[N][W] sol[temp] = 1 W -= w[temp] N = temp - 1 print (f"最大价值为:{ans} " )print (f"取得最大价值时的物品组合为:{sol} " )
结果
1 2 最大价值为:12 取得最大价值时的物品组合为:[0, 1, 0, 1]
完全背包问题 完全背包问题 (Unbounded Knapsack Problem )是背包问题的一种变种。其问题描述为:给定一个固定大小、能够携重W W W 的背包,以及N N N 个价值、重量分别为v i v_i v i 、w i w_i w i 的物品,每种物品有无限件,可以选择放多少件或不放,找出一个最佳解决方案,使得装入背包的物品总重量不超过W W W ,且总价值最大。
例
有一个容量为15 15 15 的背包,现有4 4 4 个物品,其价值和重量分别为:
求背包能装下的最大价值以及取得最大价值时的物品组合。
解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 N = 4 W = 15 v = [0 , 1 , 3 , 5 , 9 ] w = [0 , 2 , 3 , 4 , 7 ] dp = [[0 ] * (W + 1 ) for _ in range (N + 1 )] flag = [ [0 ] * (W + 1 ) for _ in range (N + 1 ) ] for i in range (1 , N + 1 ): for j in range (1 , W + 1 ): if j >= w[i]: if dp[i - 1 ][j] < dp[i][j - w[i]] + v[i]: dp[i][j] = dp[i][j - w[i]] + v[i] flag[i][j] = i else : dp[i][j] = dp[i - 1 ][j] flag[i][j] = flag[i - 1 ][j] else : dp[i][j] = dp[i - 1 ][j] flag[i][j] = flag[i - 1 ][j] ans = dp[N][W] sol = [0 ] * Nwhile flag[N][W] != 0 : N = flag[N][W] sol[N - 1 ] = 1 W -= w[N] while flag[N][W] == N: W = W - w[N] sol[N - 1 ] += 1 print (f"最大价值为:{ans} " )print (f"取得最大价值时的物品组合为:{sol} " )
结果
1 2 最大价值为:19 取得最大价值时的物品组合为:[0, 0, 2, 1]
多重背包问题 多重背包问题 (Bounded Knapsack Problem )是背包问题的一种变种。其问题描述为:给定一个固定大小、能够携重W W W 的背包,以及N N N 个价值、重量分别为v i v_i v i 、w i w_i w i 的物品,每种物品有n i n_i n i 件,可以选择放多少件或不放,找出一个最佳解决方案,使得装入背包的物品总重量不超过W W W ,且总价值最大。
例
有一个容量为25 25 25 的背包,现有4 4 4 个物品,其价值、重量、数量分别为:
求背包能装下的最大价值以及取得最大价值时的物品组合。
解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 N = 4 W = 25 v = [0 , 1 , 3 , 5 , 9 ] w = [0 , 2 , 3 , 4 , 7 ] n = [0 , 5 , 4 , 3 , 2 ] dp = [[0 ] * (W + 1 ) for _ in range (N + 1 )] flag = [ [0 ] * (W + 1 ) for _ in range (N + 1 ) ] for i in range (1 , N + 1 ): for j in range (1 , W + 1 ): for k in range (min (n[i], j // w[i]) + 1 ): if dp[i][j] < dp[i - 1 ][j - k * w[i]] + k * v[i]: dp[i][j] = dp[i - 1 ][j - k * w[i]] + k * v[i] flag[i][j] = k ans = dp[N][W] sol = [0 ] * N j = Wfor i in range (N, 0 , -1 ): sol[i - 1 ] = flag[i][j] j -= flag[i][j] * w[i]print (f"最大价值为:{ans} " )print (f"取得最大价值时的物品组合为:{sol} " )
结果
1 2 最大价值为:31 取得最大价值时的物品组合为:[0, 1, 2, 2]