01背包问题详解

01背包问题简单描述:
有一个背包和n个物品,背包的承载量为C,每件物品重量为w[i],价值为v[i].问如何装才能使背包中物品的总价值最大?
解题方法:
f[i,j]:在前i个物品中选择若干件放在承重为j的背包中,可以取得的最大的价值。
f[i,j]=MAX(f[i-1,j-w[i]]+v[i] , f[i-1,j]);
决策:为了总价值最大化,第i件物品应该放入背包中吗?

int i,j,C=10;
int p[20][20];
for(i=0;i<=C;i++)
p[0][i]=0;
for(i=1;i<=n;i++)
{
    p[i][0]=0;
    for(j=1;j<=C;j++)
    {
        if(j>=w[i])
        {
            if(p[i-1][j-w[i]]+v[i]>p[i-1][j])
                p[i][j]=p[i-1][j-w[i]]+v[i];
            else 
                p[i][j]=p[i-1][j];
        }
        else 
            p[i][j]=p[i-1][j];
    }
}

问题能深入:如何记录哪些物品被选中了?

void output_sack(int p[20][20],int x[50],int w[50],int n,int C)
{
    for(int k=n;k>=2;k--)
    {
        if(p[k][C]==p[k-1][C];
            x[k]=0;
        else 
        {
            x[k]=1;
            C=C-w[k];
        }       
    }
    x[1]=p[[1][C]?1:0;
}

解析:如果物品i没有未选中则:p[i][j]=p[i-1][j]; 所以当p[i][j]==p[i-1][j]是说明第i个物品未被选中。当第i个物品选中后,C=C-w[i],说明第i个已经确定了(背包空间考虑范围减少w[i])。

优化(从二维数组到一维数组)
因为:f[i][j]取决于f[i-1][j]和f[i-1][j-w[i]]+v[i];
所以:f_i[j]取决于f_i-1[j]和f_i-1[j-w[i]]+v[i];(f[j]外套一个for(i=1;i<=n;i++))
所以:可以用滚动的一维数组;

for(int j=0;j<=C;j++)
    f[j]=0;
for(i=1;i<=n;i++)
{
    for(j=C;j>=w[i];j--)
    {
        if(f[j-w[i]]+v[i]>f[j])
            f[j]=f[j-w[i]]+v[i];
    }
}

注意:由于f[j]有f[j]和f[j-w[i]]决定,即在二维数组中的头顶的一个数和左边的一个数决定。所以要由C->0(确保左边的一个数是上一次循环的,即只能取一次),如果由0->C,左边的值可能被改变了。

关于初始化的细节问题
没有要求必须装满:初始化f[j]=0;
要求必须装满:初始化f[0]=0,f[j]=负无穷(j>=1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值