算法--背包问题(3)

 

背包问题是一个关于最优解的经典问题。通常被讨论的最多的,最经典的背包问题是0-1背包问题(0-1 Knapsack Problem)。它是一切背包问题及相关背包问题的基础。本篇博文将详细分析0-1背包问题,并给出0-1背包问题的几种解法,同时也对0-1背包问题的内涵进行延伸,丰富其外延至完全背包问题和多重背包问题,并给出背包问题的算法实现过程,希望对大家有帮助。

一、0-1背包问题

      N件物品和一个容量为V的背包。第i件物品(每个物品只有一件)的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

(1)递归求解

算法如下:

#include <iostream>

using namespace std;

int timeval[110];

int val[110];

 

int MaxVal(int n, int s)

{

if(n<=0 || s<=0) return 0;

if( timeval[n]<=s && (MaxVal(n-1,s-timeval[n])+val[n])>MaxVal(n-1,s))

return        MaxVal(n-1,s-timeval[n])+val[n];

return MaxVal(n-1,s);

}

 

int main()

{

  int s,n,i;

  cin>>s>>n;

  for (i=1; i<=n; i++)

  cin>>timeval[i]>>val[i];

  cout<<MaxVal(n,s)<<endl;

  return 0;

}

     分析:递归求解,求解过程中的绝大部分变量存在重复求解的过程,算法的效率较低,有待改进;那怎么改进呢?最有效的是用数组保存每次计算的结果,不用重复计算,于是有二维数组求解。

(2)二维数组求解

算法如下:

#include <iostream>

using namespace std;

 

int c[110][1110];

int main()

{

int s,n,i,j;

cin>>s>>n;

int * w = new int[n+1];

int * v = new int[n+1];

for(i=1; i<=n; i++)

cin>>w[i]>>v[i];

 

  for (i=0; i<=n; i++)

  for (j=0; j<=n; j++)

  c[i][j]=0;

 

  for (i=1; i<=n; i++)

  for (j=1; j<=s; j++)

  {

  if (w[i]<=j&&c[i-1][j-w[i]]+v[i]>c[i-1][j])

  c[i][j]=c[i-1][j-w[i]]+v[i];

  else c[i][j]=c[i-1][j];

  }

 

  cout<<c[n][s]<<endl;

 

  delete w;

  delete v;

  return 0;

}

 

     分析:二维数组求解方法相比递归求解要优越很多,但是其空间复杂度依然较高,还有继续改进的余地吗?一维数组是否可行呢?答案是肯定的

(3)一维数组求解

算法分析:

#include "iostream"

using namespace std;

 

void main()

{

int w[6] = {1,2,3,5,2,1};

int v[6] = {10,12,40,40,40,15};

int c[6][10+1] = {0};

int arr[10+1] = {0};//most important for the first compution below

int i,j;

for (i=0;i<6;i++)3

{

  for (j=10;j>=w[i];j--)//notice that j>=w[i],not j>=0 注意此处与二维数组求解的区别

  {

  if (arr[j]<arr[j-w[i]]+v[i])

  {

  arr[j] = arr[j-w[i]]+v[i];

  c[i][j] = 1;

 }

  }

}

cout<<"The max value is: "<<arr[10]<<endl;

cout<<"The selected items are: ";

for (i = 6-1,j = 10;i>=0;i--)

{

  if (c[i][j])

  {

  cout<<i+1<<" ";

  j = j - w[i];

 }

}

cout<<endl;

}

    分析:在一维数组实现中将空间复杂度由O(GOODSNUM*CAPACITY)降低到O(CAPACITY)并同时进行了代码优化,同时也给出了选择物品的编号。

二、完全背包问题

      若你能给深入理解0-1背包问题和其深刻内涵,并真正明白一维数组求解背包问题后,下面的问题对你来说就相当容易了。

      完全背包问题:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

     求解分析:目标是转化为0-1背包问题。如将费用/容量为nVolume[itemIndex]的物品转化为CAPACITY/nVolume[itemIndex]相同费用和价值的物品,直接用前面的三种0-1背包问题求解。除此之外还有其它可行的求解方法吗?可以对0-1背包问题的状态方程f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}稍加分析可得完全背包问题的状态方程f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v},因而有以下算法实现过程,并最终给出了所选择的物品和选择该物品的数目。

    实现算法:

#include "iostream"

using namespace std;

void main()

{

int num,k,max;

int w[6] = {1,4,3,5,5,3};

int v[6] = {2,8,40,60,10,3};

int c[6][10+1] = {0};

int arr[10+1] = {0};//most important for the first compution below

int i,j;

for (i=0;i<6;i++)

{

for (j=10;j>=w[i];j--)//notice that j>=w[i],not j>=0

{

  num = 0;

  max = arr[j];

  for (k=1;k<=10/w[i];k++)

  {

  if (j>=k*w[i] && max<arr[j-k*w[i]]+k*v[i])

  {

  max = arr[j-k*w[i]]+k*v[i];

  num = k;

  }

  }

  arr[j] = max;

  c[i][j] = num;

  }

}

cout<<"The max value is: "<<arr[10]<<endl;

cout<<"The selected items are: "<<endl;

for (i = 6-1,j = 10;i>=0;i--)

{

  if (c[i][j])

  {

  cout<<i+1<<":"<<c[i][j]<<" ";

  j = j - c[i][j]*w[i];

  }

}

cout<<endl;

}

 

三、多重背包问题

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

      多重背包问题基本求解方式同完全背包问题,唯一的是控制参数k的限制有所不同。只需要将完全背包问题实现中的k<=CAPACITY/nVolume[itemIndex]限制条件改为k<=CAPACITY/nVolume[itemIndex] && k<= n[i]即可。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值