0-1背包问题(动态规划)


#include <iostream>
#include <cstring>
using namespace std;
 
 
const int N=15;
 
 
int main()
{
    int v[N]={0,8,10,6,3,7,2};
    int w[N]={0,4,6,2,2,5,1};
 
 
    int m[N][N];
    int n=6,c=12;
    memset(m,0,sizeof(m));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            if(j>=w[i])
                m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
 
 
            else
                m[i][j]=m[i-1][j];
        }
    }
 
 
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            cout<<m[i][j]<<' ';
        }
        cout<<endl;
    }
 
 
    return 0;
}

问题描述:0-1背包问题,给定n种物品,和一个容量为C的背包,物品 i  的重量是W[ i ] ,价值为V[ i ] ,问:应该如何选择装入背包的物品,使得装入背包中物品的价值最大?

要求,对于每一个物品,我们只能选择拿与不拿,并不能装入物品的一部分,也不能装入多个同样的物品。

解题思路:在给定空间C的情况下选择 n 个物品中的某些物品,对于每一个物品只有拿与不拿。可以想象在选择第i个物品时可以拿,也可以不拿,如果拿了第i个物品,这时候的价值可以等价于   用C-w[i]的空间选择i-1个物品的价值 + v[ i ] 。 如果不拿,这时候的价值相当于利用C的空间选择i-1个物品的价值(注意,不拿的情况下空间大,最优解不一定小于拿的情况。)。因此,对于第i个物品的选择由前边的两种情况的最大值决定。(这中解题思路是一种逆向思维,是从后边往前走的。而在计算时是由前往后计算的)

建立模型:声明一个大小为m[ n ][ c ]的二维数组,m[i][j]表示,在面对第i件物品的选择时,利用j的空间选择前i件物品能得到的最大值。根据上边的分析很容易得到m[i][j]的计算方法。

(1)如果j < w [ i ] ,这时候背包容量不足以放下第 i 件物品,只能选择不拿,这时候对于m[i][j]的选择只能有 m[ i - 1 ][ j ](在j空间中对前i-1个物品进行最优选择)所决定。

          m[ i ][ j ] = m[ i-1 ][ j ]

(2)如果j<w[i],这时候背包容量可以放下第i件物品,我们就要考虑放下这个物品能否创造最大价值,即第i件物品是否在最优解中,因此这时候我们就要比较

           m[ i - 1 ][ j ]  和  m[ i-1 ][ c - w[ i ] ] (在c - w[ i ] 的空间中面对i - 1 个物品进行最优选择,这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价             值,也是相当于为第i件物品腾出了w[i]的空间。大小来判断 i 是否选择,(而在实际计算中我们对于这两项并不了解,因此需要从前往后计算)。

状态转移方程:

if(j>=w[i])
    m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
    m[i][j]=m[i-1][j];

例:0-1背包问题:(来源:https://blog.csdn.net/xp731574722/article/details/70766804)

        在使用动态规划算法求解0-1背包问题时,使用二维数组m[ i ][ j ]存储     j 空间下面对 i 件物品的最优解  ,而使用动态规划算法求解时其实就是对二维数组m[ i ][ j ]的绘制过程。

        价值数组  V[ n ] = {8, 10, 6, 3, 7, 2}

        重量数组  M[ n ] = {4, 6, 2, 2, 5, 1}            背包容量为C = 12

        

(第一行和第一列为序号,其数值为0)
如m[2][6],在面对第二件物品,背包容量为6时我们可以选择不拿,那么获得价值仅为第一件物品的价值8,如果拿,就要把第一件物品拿出来,放第二件物品,价值10,那我们当然是选择拿。m[2][6]=m[1][0]+10=0+10=10;依次类推,得到m[6][12]就是考虑所有物品,背包容量为C时的最大价值。

代码实现:


#include <iostream>
#include <cstring>
using namespace std;
 
 
const int N=15;
 
 
int main()
{
    int v[N]={0,8,10,6,3,7,2};
    int w[N]={0,4,6,2,2,5,1};
 
 
    int m[N][N];
    int n=6,c=12;
    memset(m,0,sizeof(m));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            if(j>=w[i])
                m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
 
 
            else
                m[i][j]=m[i-1][j];
        }
    }
 
 
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            cout<<m[i][j]<<' ';
        }
        cout<<endl;
    }
 
 
    return 0;
}

到这一步,可以确定的是可能获得的最大价值,但是我们并不清楚具体选择哪几样物品能获得最大价值。

另起一个 x[ ] 数组,x[i]=0表示不拿,x[i]=1表示拿。

m[n][c]为最优值,如果m[n][c]=m[n-1][c] ,说明有没有第n件物品都一样,则x[n]=0 ; 否则 x[n]=1。当x[n]=0时,由x[n-1][c]继续构造最优解;当x[n]=1时,则由x[n-1][c-w[i]]继续构造最优解。以此类推,可构造出所有的最优解。


void traceback()
{
    for(int i=n;i>1;i--)
    {
        if(m[i][c]==m[i-1][c])
            x[i]=0;
        else
        {
            x[i]=1;
            c-=w[i];
        }
    }
    x[1]=(m[1][c]>0)?1:0;
}

参考(https://blog.csdn.net/xp731574722/article/details/70766804

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值