基础指南 之 背包问题

背包问题

背包问题是动态规划部分的代表部分,主要有 0-1 背包、完全背包等。

  1. 0-1 背包
    0-1背包中每种物品只有一件,故每种物品要么装进背包,要么没有装进背包,故曰 “0-1”。
    问题描述:n 种物品,每种物品只有一件,每种物品的重量为 wieght[i],每种物品的价值为 value[i] ,背包的总容量为 v ,求背包中能装入物品的最大价值总和。
    动态规划:dp[i][j] 表示背包容量剩下 j 时考虑装或者不装第 i 件物品的最大价值。
    状态转移方程:若 j < weight[i],此时背包容量无法装下物品 i ,则最大价值等于背包容量为 j 时考虑装或者不装物品 i-1 时的最大价值,即 dp[i][j]=dp[i-1][j];若 j>=weight[i],此时背包可以装下物品 i ,此时有两种选择,装下物品 i (但可能装不下 0到i-1之间的物品),或者不装物品 i ,即 dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])。程序如下所示:
void bag01()
{
    const int n;    //物品种类数
    const int w;    //背包最大容量
    int * weight[n+1]; //各物品重量
    int * value[n+1];  //各物品价值

    int dp[n+1][v+1];       //背包容量为v时选择装或者不装第n件物品的最大价值

    for (int i = 0; i < n+1; i++)
    {
        dp[i][0]=0;
    }
    for (int i = 0; i < v+1; i++)
    {
        dp[0][i]=0;
    }
    
    for (int i = 1; i < n+1; i++)
    {
        for (int j = 1; j < v+1; j++)
        {
            if(j<weight[i])
                dp[i][j]=dp[i-1][j];
            else
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
        }
    }
}

存储空间优化:
0-1背包中, dp[i][j]=max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]) ,新的物品装下或者不装的价值取决于 dp[i-1][j] 和 dp[i-1][j-weight[i]] ,dp[i-1][j] 在物品索引 i 的上一次循环中已计算出,而 dp[i-1][j-weight[i]] 是 i 的上一轮循环中计算出的,且在 j 的内层循环中,若将 j 的内层循环改成 从 v 到 0 的降序顺序,则 dp[i-1][j-weight[i]] 在 dp[i][j] 之前计算,而 dp[i][j-weight] 在dp[i][j] 之后计算,则可直接用 dp[j-weight] 来代替 dp[i][j-weight] ,只用一维的数据结构 dp[v] 来存储动态规划的状态。
状态转移方程:若 j >= weight[i] ,dp[j]=max(dp[j], dp[j-weight[i]]+value[i]),若 j < weight[i],则不更新 dp[i] 。
程序如下所示:

void bag01_opt()
{
    const int n;    //物品种类数
    const int w;    //背包最大容量
    int * weight[n+1]; //各物品重量
    int * value[n+1];  //各物品价值

    int dp[v+1];        //背包容量为v时最大价值

    //i=0初始化,利用只装第一件物品的情况初始化
    for(int j=0;j<=w;j++)
    {
        if(j<weight[0])
        {
            dp[j]=0;
        }
        else
        {
            dp[j]=value[0];
        }
    }

    //将 w 维度上的循环反序,保证计算dp[j]时的dp[j-weight[i]]是i-1轮循环保留下的,而不是i轮循环保存的
    //每个物品只有一件,故要保证 dp[j-weight[i]] 是i-1轮留下来的
    for(int i=1;i<n+1;i++)
    {
        for(int j=w;j>=weight[i];j--)
        {
            //此处不用判断,循环已经做了控制,大于才更行dp,小于不用更新
            dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
}
  1. 完全背包
    问题描述:n 种物品,每种物品有无数件,每种物品的重量为 wieght[i],每种物品的价值为 value[i] ,背包的总容量为 v ,求背包中能装入物品的最大价值总和。
    完全背包按照其思路可以用一个而为数组实现状态转移方程:
    f[i][j]=max{f[i-1][v-kweight[i]]+kvalue[i] | 0<=k && c[i]<=v};
    与0-1背包中原理类似,可将二维数组用一维数组表示:f[j]=max{f[j],f[j-weight[i]]+value[i] | j>=weight[i]};因为每种物品的数量无限,则无需保证 dp[j-weight[i]] 是 i 的哪一轮循环,则 j 的循环可以从 0 到 v ,程序如下所示:
void bag_full()
{
    const int n;    //物品种类数
    const int w;    //背包最大容量
    int * weight[n+1]; //各物品重量
    int * value[n+1];  //各物品价值

    int dp[v+1];        //背包容量为v时最大价值

    for(int i=0;i<n+1;i++)
    {
        dp[i]=0;
    }

    //每件物品有多件,故得保证dp[j-weight[i]]是当前考虑第i轮循环产生的最新结果
    for(int i=1;i<n+1;i++)
    {
        for(int j=0;j<V+1;j++)
        {
            //大于才更新dp
            if(j>=weight[i])
            {
                dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值