JS禁止查看网页源代码的简单实现方法

【算法笔记】背包模板-算法详解

01背包

N 件物品和一个容量为 M 的背包。第 i 件物品所耗费的空间是 w[i],得到的价值是 v[i]。求解将哪些物品装入背包可使价值总和最大。

  • “求什么设什么”,我们用 f[i][j] 表示前 i 件物品耗费空间为 j 可得到的最大价值。显然,答案是 f[N][M]
  • 那么状态转移方程呢?思考一下。对于第 i 件物品,有取和不取两种情况。
  • 如果当前这件物品不取,那么其实前 i1 件物品耗费的总空间就是 j,得到的价值不变;
  • 如果当前这件物品取,那么前 i1 件物品耗费的总空间就是 jw[i],得到的价值就是 f[i1][jw[i]]+v[i]
  • 所以可得转移方程:f[i][j]=max(f[i1][j]f[i1][jw[i]]+v[i])
  • 不过,二维的空间,是不是有些浪费?
  • 其实,我们可以省去 i 这一维,那么转移方程就变成了:f[j]=max(f[j],f[jw[i]]+v[i])
  • 不过,省去了这一维,就出现了一个细节需要注意:要倒叙枚举 j,而不是正序枚举,因为正序枚举在枚举 f[j]f[jw[i]] 已经被覆盖过了,那么就有可能取过了第 i 件物品,不满足动态规划的特性:无后效性。(自己手动推一下,会更理解)

Code

scanf("%d %d",&n,&m); for (int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]); for (int i=1;i<=n;i++) for (int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+v[i]);

完全背包

N 件物品和一个容量为 M 的背包,每件物品都有无限件。放入 第 i件物品耗费的空间是 w[i],得到的价值是 v[i]。求解将哪些物品放入背包可使价值总和最大;

  • 可得转移方程f[i][j]=max(f[i1][j],f[i][jvi]+wi)
  • 完全背包怎么压缩空间呢?其实就是 01 背包的代码,把 j 改成正序枚举就好了。为什么可以这样做?
  • 因为完全背包可以取无限次,枚举 f[j] 前第 i 件物品能够已经取过,符合完全背包

Code

scanf("%d %d",&n,&m); for (int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]); for (int i=1;i<=n;i++) for (int j=w[i];j<=m;j++) f[j]=max(f[j],f[j-w[i]]+v[i]);

多重背包

N 种物品和一个容量为 M 的背包。第i种物品最多有 n[i] 件可用,每件费用是 w[i],价值是 v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  • 我们可以用暴力的方法枚举,对于第i种物品有 n[i]+1 种策略:取 0 件,取 1 件……取 n[i] 件,f[i][v]=max(f[i1][vkw[i]]+kv[i])(0<=k<=n[i]
  • 可是这种方法会超时 QAQ,所以我们要用二进制优化

Code

  • 以下是一种简洁的方法:
for (int i=1;i<=n;i++) for (int j=1;n[i]>0;n[i]-=j,j=min(j*2,n[i])) for (int k=v;k>=w[i]*j;k--) f[k]=max(f[k],f[k-w[i]*j]+v[i]*j);

二维费用背包

对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为 a[i]b[i]。两种代价可付出的最大值(两种背包容量)分别为VU。物品的价值为v[i]

  • 费用加了一维,只要状态也加一维即可;
  • 状态转移方程:f[i][j][k]=max(f[i1][j][k],f[i1][ja[i]][kb[i]]+v[i])
  • 同样,可以压缩空间:f[v][u]=max(f[[j][k],f[ja[i]][kb[i]]+v[i])

Code

for (int i=1;i<=n;i++) for (int j=V;j>=a[i];j--) for (int k=U;k>=b[i];k--) f[j][k]=max(f[j][k],f[j-a[i]][k-b[i]]+v[i]);

分组背包

N 件物品和一个容量为 V 的背包。第i件物品的费用是 w[i],价值是v[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  • 其实对于这种题目,我们只是多一重循环枚举组中的物品,这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。具体看代码;

Code

for 所有的组k for (int j=V;j>=0;j--) for 所有的i属于组k f[j]=max{f[j],f[j-c[i]]+w[i]}

01背包

N 件物品和一个容量为 M 的背包。第 i 件物品所耗费的空间是 w[i],得到的价值是 v[i]。求解将哪些物品装入背包可使价值总和最大。

  • “求什么设什么”,我们用 f[i][j] 表示前 i 件物品耗费空间为 j 可得到的最大价值。显然,答案是 f[N][M]
  • 那么状态转移方程呢?思考一下。对于第 i 件物品,有取和不取两种情况。
  • 如果当前这件物品不取,那么其实前 i1 件物品耗费的总空间就是 j,得到的价值不变;
  • 如果当前这件物品取,那么前 i1 件物品耗费的总空间就是 jw[i],得到的价值就是 f[i1][jw[i]]+v[i]
  • 所以可得转移方程:f[i][j]=max(f[i1][j]f[i1][jw[i]]+v[i])
  • 不过,二维的空间,是不是有些浪费?
  • 其实,我们可以省去 i 这一维,那么转移方程就变成了:f[j]=max(f[j],f[jw[i]]+v[i])
  • 不过,省去了这一维,就出现了一个细节需要注意:要倒叙枚举 j,而不是正序枚举,因为正序枚举在枚举 f[j]f[jw[i]] 已经被覆盖过了,那么就有可能取过了第 i 件物品,不满足动态规划的特性:无后效性。(自己手动推一下,会更理解)

Code

scanf("%d %d",&n,&m); for (int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]); for (int i=1;i<=n;i++) for (int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+v[i]);

完全背包

N 件物品和一个容量为 M 的背包,每件物品都有无限件。放入 第 i件物品耗费的空间是 w[i],得到的价值是 v[i]。求解将哪些物品放入背包可使价值总和最大;

  • 可得转移方程f[i][j]=max(f[i1][j],f[i][jvi]+wi)
  • 完全背包怎么压缩空间呢?其实就是 01 背包的代码,把 j 改成正序枚举就好了。为什么可以这样做?
  • 因为完全背包可以取无限次,枚举 f[j] 前第 i 件物品能够已经取过,符合完全背包

Code

scanf("%d %d",&n,&m); for (int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]); for (int i=1;i<=n;i++) for (int j=w[i];j<=m;j++) f[j]=max(f[j],f[j-w[i]]+v[i]);

多重背包

N 种物品和一个容量为 M 的背包。第i种物品最多有 n[i] 件可用,每件费用是 w[i],价值是 v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  • 我们可以用暴力的方法枚举,对于第i种物品有 n[i]+1 种策略:取 0 件,取 1 件……取 n[i] 件,f[i][v]=max(f[i1][vkw[i]]+kv[i])(0<=k<=n[i]
  • 可是这种方法会超时 QAQ,所以我们要用二进制优化

Code

  • 以下是一种简洁的方法:
for (int i=1;i<=n;i++) for (int j=1;n[i]>0;n[i]-=j,j=min(j*2,n[i])) for (int k=v;k>=w[i]*j;k--) f[k]=max(f[k],f[k-w[i]*j]+v[i]*j);

二维费用背包

对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为 a[i]b[i]。两种代价可付出的最大值(两种背包容量)分别为VU。物品的价值为v[i]

  • 费用加了一维,只要状态也加一维即可;
  • 状态转移方程:f[i][j][k]=max(f[i1][j][k],f[i1][ja[i]][kb[i]]+v[i])
  • 同样,可以压缩空间:f[v][u]=max(f[[j][k],f[ja[i]][kb[i]]+v[i])

Code

for (int i=1;i<=n;i++) for (int j=V;j>=a[i];j--) for (int k=U;k>=b[i];k--) f[j][k]=max(f[j][k],f[j-a[i]][k-b[i]]+v[i]);

分组背包

N 件物品和一个容量为 V 的背包。第i件物品的费用是 w[i],价值是v[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  • 其实对于这种题目,我们只是多一重循环枚举组中的物品,这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。具体看代码;

Code

for 所有的组k for (int j=V;j>=0;j--) for 所有的i属于组k f[j]=max{f[j],f[j-c[i]]+w[i]}

__EOF__

本文作者Admin-zx
本文链接https://www.cnblogs.com/Michael-zx/p/12334836.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Z__X  阅读(310)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示