0-1背包问题(DP)

目录

1.问题描述

2.分析问题

3.过程推理

4.代码


1.问题描述

给定一个物品集合s={1,2,3,…,n},物品i的重量是wi,其价值是vi,背包的容量为W,即最大载重量不超过W。在限定的总重量W内,我们如何选择物品,才能使得物品的总价值最大。

  • 如果物品不能被分割,即物品i要么整个地选取,要么不选取; 不能将物品i装入背包多次,也不能只装入部分物品i,则该问题称为0—1背包问题,适合使用DP。
  • 如果物品可以拆分,则问题称为背包问题,适合使用贪心算法。

2.分析问题

假设xi表示物品i装入背包的情况

  • 当xi=0时,表示物品没有装入背包;
  • 当xi=1时,表示把物品装入背包。

约束方程:         ≤W

目标函数:

因此问题就归结为找到一个满足上述约束方程, 并使目标函数达到最大的解向量: X={x1,x2,…,xn}

0-1背包问题的子问题: i≤k≤n的最优值为p(i,j)

p(i,j)是背包容量为j,可选物品为i,i+1,…,n时0-1背包问题的最优值!

此时一定要理解公式p(i,j)的意思


p(n,j)表示背包容量是j,可放物品只能是n时的最优值

  • 若j<wn,则背包放不下n,p(n,j)=0
  • 若j>=wn,则背包放一个n,p(n,j)=vn

所以p(i,j)的递归方程:

  • 若j<wi,则背包放不下i物品,再看看能不能放i+1,…,n的物品,p(i,j)=p(i+1,j)
  • 若j>=wi
    • 背包放一个i物品,剩余容量为j-wi,再放i+1,…,n的物品,p(i,j)=p(i+1,j-wi)+vi
    • 背包不放i物品(可能i物品的性价比不高),p(i,j)=p(i+1,j)

3.过程推理

W=5 

1

2

3

4

重量w

2

1

3

2

价值v

12

10

20

15

  • p[2][1]=max(p[3][1],p[3][0]+10)=10;
  • p[2][2]=max(p[3][2],p[3][1]+10)=15;
  • p[2][3]=max(p[3][3],p[3][2]+10)=25;
  • p[2][4]=max(p[3][4],p[3][3]+10)=30;
  • p[2][5]=max(p[3][5],p[3][4]+10)=35;
  • p[1][5]=max(p[2][5],p[2][3]+12)=37;

从下向上,从左到右填表

第4行,只考虑物品4,物品4的重量是2,价值是15

  • 背包容量为0和1的时候,放不进去4,因此p[4][0]=p[4][1]=0
  • 背包容量为2,3,4,5的时候,放进一个4,因此p[4][2]=p[4][3]=p[4][4]=p[4][5]=15

第3行,只考虑物品4和3,物品4的重量2,价值15;物品3的重量3,价值20

以p[3][5]为例,代入上述方程可得:

  • p[3][5]=max(p[4][5],p[4][5-3]+20)=35
    • 5-3的3是物品3的重量,20是物品3的价值
    • 意思是j(5)>wi(3),此时物品3可以放进背包
      • 如果物品3性价比不高,就选择不放进来,p[3][5]=p[4][5]
      • 如果物品3性价比高,就选择放进背包,p[3][5]=p[4][2]+20,意思是物品3放入背包,背包容量变为2,而价值增加20
  • p[3][5]=p[4][5]=15
    • 意思是如果j<wi,此时物品i不能放进背包

我们最终需要的结果放在了p[1][5]中

求p[1][5]需要用到第二行的数据,而p[1][0~4]的数据都没有用,可以置0

  • p[1][5]=max(p[2][5],p[2][5-3]+20)=35
  • p[1][5]=p[4][5]=15

n | j

0

1

2

3

4

5

1

0

0

0

0

0

37

2

0

10

15

25

30

35

3

0

0

15

20

20

35

4

0

0

15

15

15

15

4.代码

#include <iostream>

using namespace std;

#define NUM 50//物品数量的上限
#define CAP 1500//背包容量的上限
int w[NUM];//物品重量
int v[NUM];//物品价值
int p[NUM][CAP];//存放结果的数组

//求最优值
//c是背包容量,n是物品数量
void dg(int c,int n){
    int jmax=min(w[n]-1,c);//分界点
    //整型,使得分界点是比物品n的重量刚好小一点的容量
    //初始化
    for(int j=0;j<=jmax;j++)
        p[n][j]=0;
    for(int j=w[n];j<=c;j++)
        p[n][j]=v[n];
    for(int i=n-1;i>1;i--){//行,从下往上
        jmax=min(w[i]-1,c);
        for(int j=0;j<=jmax;j++)//列,放不开物品i
            p[i][j]=p[i+1][j];
        for(int j=w[i];j<=c;j++)//列,放得开物品i
            p[i][j]=max(p[i+1][j],p[i+1][j-w[i]]+v[i]);
    }
    //第一行单独求
    p[1][c]=p[2][c];
    if(c>=w[1])
        p[1][c]=max(p[1][c],p[2][c-w[1]]+v[1]);
}

//求最优解
void trace(int c,int n,int x[]){
    //判断p[i][j]和p[i+1][j]是否相等
    for(int i=1;i<n;i++){
        if(p[i][c]==p[i+1][c])//相等,说明物品i没有放进去
            x[i]=0;
        else{
            x[i]=1;//不等,说明物品i放进背包
            c=c-w[i];//背包现有容量=背包原容量-减去物品i重量
        }
        x[n]=p[n][c]?1:0;//p[n][c]为真则返回1,否则返回0
    }
}
int main()
{
    int n,c;
    cin>>c>>n;
    int x[n];
    for(int i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    dg(c,n);
    cout<<p[1][5]<<endl;
    trace(c,n,x);
    for(int i=1;i<=n;i++)
        if(x[i])
            cout<<i<<" ";
    return 0;
}

输入:

 输出:

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值