背包问题

根据维基百科,背包问题(Knapsack problem)是一种组合优化的NP完全(NP-Complete,NPC)问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。NPC问题是没有多项式时间复杂度的解法的,但是利用动态规划,我们可以以伪多项式时间复杂度求解背包问题。

01背包问题
定一个背包,容量为V。一共有n件物品,第i件物品的价值为w[i],体积为v[i]。求背包中能装入物品的价值总和最大为多少。
使用动态规划求解。设置dp[i][j]表示前i件物品,在总体积不超过j的情况下,价值总和最大为dp[i][j]。则有dp[0][0-V]=0。dp[0-n][0]=0。当i>0时,每一件物品都有装入和不装入两种状态,而装入的前提条件是v[i]<=背包剩余空间。则可以写出状态转移方程:

(1)if (j >= v[i]) , dp[i][j] = Math.max( dp[i - 1][ j - v[i-1] ] + w[i-1],dp[i-1][j] )。
(2)if(j<v[i]),dp[i][j]=dp[i-1][j]。

//通过状态转移方程写出代码:

function bag_01(w,v,V){
  let dp=[];
  // 初始化
  for(let i=0;i<=w.length;i++){
    dp[i]=[];
    dp[i][0]=0;
  }
  for(let i=0;i<=V;i++){
    dp[0][i]=0;
  }

  // 动态规划填表
  for(let i=1;i<=w.length;i++){
    for(let j=1;j<=V;j++){
      if(j>=v[i-1]){
        dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[i-1]]+w[i-1]);
      }else{
        dp[i][j]=dp[i-1][j];
      }
    }
  }
  return dp[w.length][V];
}

由以上可以看出,dp[i][j]的状态取决于dp[i-1][j-v[i]]或者dp[i-1][j],仅与dp[i-1]有关。则我们可以进行空间优化,每次只记录第二维的数据,后面循环的数据进行覆盖,每次dp[j]的值都是最新的值。则最后dp[V]的值就是我们想要的值。

let dp=[];
  // 初始化
  for(let i=0;i<=V;i++){
    dp[i]=0;
  }

  for(let i=1;i<=w.length;i++){
    for(let j=V;j>=1;j--){//防止前面的数据被覆盖,必须逆向填表
      if(j>=v[i-1]){
        dp[j]=Math.max(dp[j],dp[j-v[i-1]]+w[i-1]);
      }
    }
  }
  return dp[V];

完全背包问题
与01背包的不同之处在于,完全背包不同种的物品都有无限个。定一个背包,容量为V。一共有n种物品,第i种物品的价值为w[i],体积为v[i],每种物品有无限个。求背包中能装入物品的价值总和最大为多少。
此时与求解01背包问题类似,只不过状态转移方程改变了。

if(j>=v[i-1])  dp[i][j]=Math.max(dp[i-1][j],dp[i][j-v[i-1]]+w[i-1])
else dp[i][j]=dp[i-1][j]

//通过状态转移方程写出代码:

let dp=[];
  // 初始化
  for(let i=0;i<=w.length;i++){
    dp[i]=[];
    dp[i][0]=0;
  }
  for(let i=0;i<=V;i++){
    dp[0][i]=0;
  }

  // 动态规划填表
  for(let i=1;i<=w.length;i++){
    for(let j=1;j<=V;j++){
      if(j>=v[i-1]){
        dp[i][j]=Math.max(dp[i-1][j],dp[i][j-v[i-1]]+w[i-1]);//仅仅这里与01背包有区别
      }else{
        dp[i][j]=dp[i-1][j];
      }
    }
  }
  return dp[w.length][V];

完全背包也有空间优化手段,优化时需要正向填表,因为完全背包的dp[i][j]取决于dp[i][j-v[i-1]]和dp[i-1][j],则前面数据需要使用被覆盖后的数据。

let dp=[];
  // 初始化
  for(let i=0;i<=V;i++){
    dp[i]=0;
  }

  for(let i=1;i<=w.length;i++){
    for(let j=1;j<=V;j++){//需要考虑前面的数据被覆盖,必须正向填表
      if(j>=v[i-1]){
        dp[j]=Math.max(dp[j],dp[j-v[i-1]]+w[i-1]);
      }
    }
  }
  return dp[V];

多重背包问题
思路与01背包和完全背包大致相等,每种物品多了一个数量限制。定一个背包,容量为V。一共有n种物品,第i种物品的价值为w[i],体积为v[i],数量为N[i]。求背包中能装入物品的价值总和最大为多少。
状态转移方程变为:

if(j>=v[i-1]) 
for(let k=1;k<=Math.min(Math.floor(j/v[i]),N[i-1]);k++) {
  dp[i][j]=Math.max(dp[i-1][j],dp[i][j-k*v[i-1]]+w[i-1]*k
}

else dp[i][j]=dp[i-1][j]
全部评论

相关推荐

03-28 16:43
佛山大学 Java
java全国可飞:简历2.0,各位佬看看,这样可以吗查看图片
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务