动态规划:01背包问题-创新互联

一、什么是01背包问题?

举个例子,你要去一个水果摊拿水果,每种水果都有对应的两种属性:占用的体积V和蕴含的价值W。而你的背包体积为N。老板说:每种水果只能拿一个!因此对于咱们肯定得想一种搭配方式使得拿的水果总体积不超过背包容积,但是价值总和达到大!

坚守“ 做人真诚 · 做事靠谱 · 口碑至上 · 高效敬业 ”的价值观,专业网站建设服务10余年为成都成都铜雕雕塑小微创业公司专业提供成都企业网站建设营销网站建设商城网站建设手机网站建设小程序网站建设网站改版,从内容策划、视觉设计、底层架构、网页布局、功能开发迭代于一体的高端网站建设服务。

  核心思想:

  f[i][j]:表示所有选法集合中,只从前i个物品中选,并且总体积不大于j的选法的集合,它的值是这个集合中每一个选法的大值。

  对于01背包问题选择方法的集合可以分成2种:
①不选第i个物品,并且总体积不大于j的集合所达到的大值:f[i-1][j]
②选择1~i个物品,并且总体积不大于j的集合所达到的大值f[i][j]

对于第二种情况我们很难计算,因此需要思考从另一个角度解决问题。当选择1~i个物品,总体积不大于j的集合的大值可以转化成选择1~i-1个物品,总体积不大于j-V[i]的集合+最后一个物品的价值:f[i-1][j-V[i]]+w[i]

因此总结:f[i][j]= Max{f[i-1][j],f[i-1][j-v[i]]+w[i]}!!! 二、01背包例题

  #ACWing 2

代码:

二维数组:

#includeusing namespace std;

const int N=1010;
int v[N],w[N],f[N][N];
int n,m;

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

  printf("%d",f[n][m]); 
  return 0;
}

##注意 :为什么i和j从1开始遍历,因为如果i或j不管哪个为0,f[i][j]其实都等于0!!

一维数组: 优化版

#includeusing namespace std;

const int N=1010;
int v[N],w[N],f[N];
int n,m;

int main()
{
  scanf("%d%d",&n,&m);
  
  for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
  
  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]);
    }
    
  printf("%d",f[m]);
  return 0;
}

如何优化:

  从二维做法中可以看出f[i] [j]大值的更新只用到了 f[i-1] [j],即 f[i-2][j] 到 f[0][j] 是没有用的。

所以第二层循环可以直接从v[i] 开始。

for (int i = 1; i<= n; i++) {
    for (int j = v[i]; j<= m; j++) {
        f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
    }
}

二维优化到一维后:

如果删掉f[i]这一维,结果如下:如果j层循环时递增的,则是错误的

for (int i = 1; i<= n; i++) {
    for (int j = v[i]; j<= m; j++) {
        f[j] = max(f[j], f[j - v[i]] + w[i]);
    }
}

模拟结果:

可以看出处于 i == 1 这一层,即物品只有一件,不存在单件物品满足价值为6,8,10的,所以已经出错了。

为什么一维情况下枚举背包容量需要逆序?
在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。

例如,一维状态第i轮对体积为 3 的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该是f[i - 1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。

  如果 j 层循环是逆序的:

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]);
        }
    }

模拟结果: 

模拟一下发现没有错误,即逆序就可以解决这个优化的问题了 

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


文章名称:动态规划:01背包问题-创新互联
文章路径:http://csdahua.cn/article/eisdc.html
扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流