动态规划—背包问题
发布日期:2021-05-08 11:10:52 浏览次数:18 分类:精选文章

本文共 2216 字,大约阅读时间需要 7 分钟。

背包问题

      背包问题(Knapsack problem)是一种组合优化的NP完全(NP-Complete,NPC)问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。本章所有的背包问题讨论均使用
集合的思想求解背包问题。一般来讲,背包问题有以下几种分类:

1.01背包

2.完全背包
3.多重背包
4.分组背包

01背包问题

一般01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。

用状态表示和状态计算解决背包问题:

状态表示:f[i][j]表示使用前i个物品,体积不超过j的最大价值

状态转移:状态转移从最后一个物品分析,考虑不重不漏的情况:
1.分为使用第i个物品;
2.不使用第i个物品。

f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] ; 不 选 第 i 个 物 品 f [ i − 1 ] [ j − v i ] + w i ; 选 第 i 个 物 品 f[i][j] =max\begin{cases} f[i - 1][j];不选第i个物品\\ f[i - 1][j - v_i] + w_i;选第i个物品\\\end{cases} f[i][j]=max{
f[i1][j];if[i1][jvi]+wi;i
     不选第i个物品则体积仍为j,即直接从f[i - 1][j]转移,若选择第i个物品,则体积需要减去i个物品对应的vi,加上对应的价值wi

//二维空间for(int i = 1; i <= n; i++)	for(int j = 0; j <= m; j++)//m表示可容纳的最大体积		if(v[i] > j) f[i][j] = f[i - 1][j];		else f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);//v[i] <= j

     从转移状态方程来看第i层的状态方程都是从i-1层转移过来的,可以从空间上优化一下。去掉第一维,但是要保证第i层的f是使用第i-1层的f来更新,若单纯去掉第一维:

//!!!错误代码for(int i = 1; i <= n; i++)	for(int j = 0; j <= m; j++)//m表示可容纳的最大体积		if(v[i] > j) f[j] = f[j];		else f[j] = max(f[j], f[j - v[i]] + w[i]);//v[i] <= j

     在上述代码第i层时,较大的j是用更小的j来更新的,但是f[j]是从小到大来维护,因此上次代码虽然省略了第一维,但是第i层的f[j]的更新是利用第i层的f[j - v[i]]来更新的,应该使用第i-1层的!为了避免这个问题,可以将上述第二层循环从大到小来枚举即可解决这个问题:

for(int i = 1; i <= n; i++)	for(int j = m; j >= v[i]; j--)//从大到小来枚举		f[j] = max(f[j], f[j - v[i]] + w[i]);//v[i] <= j

     从大到小枚举的时候,更新f[j]时使用的f[j - v[i]]在第i层还未更新,则使用的f[j - v[i]]是上一层即i-1的状态。

二维费用01背包问题

有 N 件物品和一个容量是 V 承重是 M 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,重量是 mi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,总质量不超过背包承重,且总价值最大。输出最大价值。

用状态表示和状态计算解决背包问题:

状态表示:f[i][j][k]表示使用前i个物品,体积不超过j,重量不超过k的最大价值

状态转移:状态转移从最后一个物品分析,考虑不重不漏的情况:
1.分为使用第i个物品;
2.不使用第i个物品。

f [ i ] [ j ] [ k ] = m a x { f [ i − 1 ] [ j ] [ k ] ; 不 选 第 i 个 物 品 f [ i − 1 ] [ j − v i ] [ k − m i ] + w i ; 选 第 i 个 物 品 f[i][j][k] =max\begin{cases} f[i - 1][j][k];不选第i个物品\\ f[i - 1][j - v_i][k-m_i] + w_i;选第i个物品\\\end{cases} f[i][j][k]=max{
f[i1][j][k];if[i1][jvi][kmi]+wi;i
则实际代码可以使用两维空间,但是两个限制都要从大到小枚举:

for(int i = 1; i <= n; i++)	for(int j = V; j >= v1[i]; j--)//从大到小来枚举		for(int k = M; k >= v2[i]; k--)			f[j][k] = max(f[j][k], f[j - v1[i]][k - v2[i]] + w[i]);
上一篇:数论相关内容总结——同余
下一篇:STL源码剖析—traits编程技巧

发表评论

最新留言

关注你微信了!
[***.104.42.241]2025年04月15日 07时33分33秒