目录
前言
单一背包问题(0-1 Knapsack Problem)是组合优化中的一个经典问题,它可以形式化为:给定一组物品,每个物品都有一个重量和一个价值,目标是选择一些物品放入背包中,使得背包中物品的总价值最大,同时不超过背包的最大承重限制。在这个问题中,每个物品只能选择放或不放,不能分割。
解决单一背包问题的方法包括动态规划、回溯算法和分支限界法等。动态规划是解决这类问题的常用方法,它通过构建一个二维数组来存储子问题的解,并使用状态转移方程来填充这个数组。状态转移方程通常表示为 dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]),其中 dp[i][j] 表示前 i 个物品放入容量为 j 的背包中所能获得的最大价值,w[i] 和 v[i] 分别表示第 i 个物品的重量和价值。
在实际应用中,单一背包问题可以模拟旅行行李打包、投资组合选择、货物装载与运输、项目选择与管理、供应链管理、能源管理、课程安排和紧急救援物资分配等场景。
例题
题目描述
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi ,价值是 wi 。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入输出格式
输入格式
第一行两个整数N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
一个非负整数,代表结果。
输入输出样例1
输入
4 5
1 2
2 4
3 4
4 5
输出
8
输入输出样例2
输入
1 2
2 2
输出
2
解题思路
-
初始化:首先,代码定义了背包的容量
V
和物品的数量N
。然后,它为物品的重量和价值分配了动态数组weights
和values
,以及一个二维数组lst
来存储动态规划过程中的状态值。 -
输入读取:代码使用
scanf
函数从标准输入读取物品的数量、背包容量以及每个物品的重量和价值。 -
动态规划填充:代码通过两层嵌套循环来填充动态规划数组
lst
。外层循环遍历物品,内层循环遍历背包的容量。对于每个容量和每个物品,代码检查当前物品是否可以放入背包中(即当前容量是否大于或等于该物品的重量)。如果可以,代码计算放入当前物品和不放入当前物品两种情况下的最大价值,并更新lst
数组中相应位置的值。 -
结果输出:填充完成后,代码通过
printf
函数输出背包的最大价值,即lst[N][V]
。
具体代码
C语言代码
#include<stdio.h>
#include<stdlib.h>
#define maxlen 100
int main(void)
{
int N, V;
scanf("%d%d", &N, &V);
int lst[maxlen][maxlen] = { 0 };
int* weights = (int*)malloc(N * sizeof(int));
int* values = (int*)malloc(V * sizeof(int));
for (int i = 0; i < N; i++)
{
int s, r;
scanf("%d%d", &s, &r);
weights[i] = s;
values[i] = r;
}
for(int i = 1;i<N+1;i++)
for (int j = 1; j < V + 1; j++)
{
if (j < weights[i - 1])
lst[i][j] = lst[i - 1][j];
else
{
int datas1 = lst[i - 1][j];
int datas2 = lst[i - 1][j - weights[i - 1]] + values[i - 1];
if (datas1 > datas2)
lst[i][j] = datas1;
else
lst[i][j] = datas2;
lst[i][j]=(datas1>datas2)?datas1:datas2;
}
}
printf("%d", lst[N][V]);
return 0;
}
python代码
N,V = [int(i) for i in input().split()]
lst = []
weights = []
values = []
for i in range(N):
w,v = [int(i) for i in input().split()]
weights.append(w)
values.append(v)
for i in range(N+1):
lst.append([0]*(V+1))
for i in range(1,N+1):
for j in range(1,V+1):
if j<weights[i-1]:
lst[i][j] = lst[i-1][j]
else:
datas1 = lst[i-1][j]
datas2 = lst[i-1][j-weights[i-1]]+values[i-1]
lst[i][j] = max(datas1,datas2)
print(lst[N][V])
总结
单一背包问题是组合优化问题中的一个经典问题,它可以通过动态规划算法高效解决。在这个问题中,目标是选择一组物品放入背包中,使得背包的总价值最大,同时不超过背包的容量限制。动态规划算法通过构建一个二维状态数组来存储子问题的最优解,并通过状态转移方程递归地求解每个子问题,最终得到整个问题的最优解。
动态规划解决单一背包问题的关键在于定义合适的状态和状态转移方程。状态通常定义为dp[i][j]
,表示前i
个物品放入容量为j
的背包中所能获得的最大价值。状态转移方程为dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
,其中w[i]
和v[i]
分别是第i
个物品的重量和价值。如果当前背包容量不足以容纳第i
个物品,则状态转移方程简化为dp[i][j] = dp[i-1][j]
。
动态规划算法的时间复杂度为O(N*V)
,其中N
是物品的数量,V
是背包的容量。空间复杂度也是O(N*V)
,但可以通过使用一维数组进行空间优化,将空间复杂度降低到O(V)
。
在实际应用中,动态规划不仅能够解决单一背包问题,还可以扩展到完全背包问题、多重背包问题等变体。动态规划的思想在解决这类优化问题时展现了其强大的能力,通过将大问题分解为小问题并存储子问题的解,动态规划避免了重复计算,提高了算法效率。