完全背包问题的解决方法______闫氏 DP 分析法

完全背包问题的解决方法______闫氏 DP 分析法

完全背包问题的解决方法:

闫氏   D P \ DP  DP 分析法:
通过集合划分,我们可以得到第   i \ i  i 个物品有两种状态:
 1.选   1 − T \ 1 - T  1T 个,最优解为前   i − 1 \ i− 1  i1 个物品的所有选择中,还能选取当前   k \ k  k 个第   i \ i  i 个物品的最大价值加上   k \ k  k 个第   i \ i  i 个物品本身的价值,即
  d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − k ∗ v [ i ] ] + k ∗ w [ i ] ) , ( 1 ≤ k ≤ T ) \ dp[ i ][ j ]= max( dp[ i][ j], dp[ i− 1][ j− k∗ v[i]]+k∗w[i]),(1≤k≤T)  dp[i][j]=max(dp[i][j],dp[i1][jkv[i]]+kw[i]),(1kT)
 2.不选,最优解为前   i − 1 \ i−1  i1个物品的所有选择中的最大价值,即   d p [ i ] [ j ] = d p [ i − 1 ] [ j ] \ dp[i][j]=dp[i−1][j]  dp[i][j]=dp[i1][j]
若使用枚举的方式来选取   1 − k \ 1−k  1k个物品 i的情况,会超时,故需分析当前的状态转移方程能否被优化。
我们令该朴素转移方程为 A式:
  d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v ] + w , d p [ i − 1 ] [ j − 2 v ] + 2 w , ⋯ , d p [ i − 1 ] [ j − k v ] + k w ) \ dp[i][j]=max(dp[i−1][j],dp[i−1][j−v]+w,dp[i−1][j−2v]+2w,⋯,dp[i−1][j−kv]+kw)  dp[i][j]=max(dp[i1][j],dp[i1][jv]+w,dp[i1][j2v]+2w,,dp[i1][jkv]+kw)
将左式的   j \ j  j替换成   j − v \ j−v  jv后,得到 B式:
  d p [ i ] [ j − v ] = m a x ( d p [ i − 1 ] [ j − v ] , d p [ i − 1 ] [ j − 2 v ] + w , ⋯ , d p [ i − 1 ] [ j − k v ] + ( k − 1 ) w ) \ dp[i][j−v]=max(dp[i−1][j−v],dp[i−1][j−2v]+w,⋯,dp[i−1][j−kv]+(k−1)w)  dp[i][jv]=max(dp[i1][jv],dp[i1][j2v]+w,,dp[i1][jkv]+(k1)w)
仔细观察可以发现,A式从第二项开始与 B 式很相似,两者之间的差异仅为一个 w。
因此,将 A式右边第二项开始替换成 B 式加上一个 w 后,可得 C式:
  d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − v ] + w ) \ dp[i][j]=max(dp[i−1][j],dp[i][j−v]+w)  dp[i][j]=max(dp[i1][j],dp[i][jv]+w)

在这里插入图片描述3. 完全背包问题

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];//表示从前i个物品中选,总体积不超过j的方案的集合

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
	
	//完全背包模板
	//遍历物品数,当前是第i件物品
    for (int i = 1; i <= n; i ++ )//物品从第1件开始
    {
    	//遍历背包容量(从小到大遍历,只要空间够,第i件物品想拿几个,就拿几个)
        for (int j = 0; j <= m; j ++ )//空间从0开始
        {
            f[i][j] = f[i-1][j];
            if( j >= v [i] )
            {
                f[i][j] = max(f[i-1][j], f[i][j - v[i]] + w[i]);//公式推导出来的
            }
        }
    }

    cout << f[n][m] << endl;
    return 0;
}

改成一维,如下:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N];//不超过总体积j的方案的集合

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

	//完全背包模板
	//遍历物品数,当前是第i件物品
    for (int i = 1; i <= n; i ++ ){
    	//遍历背包容量(从小到大遍历,只要空间够,第i件物品想拿几个,就拿几个)
        for (int j = v[i]; j <= m; j ++ ){
        		//f[j] = f[j];  等式右边的f[j]表示 : 未更新前的,前i个物品中选,总体积不超过j的方案的集合 ; 等式左边表示更新后的 前i个物品中选,总体积不超过j的方案的集合。所以,与f[i][j] = f[i-1][j];等效
            f[j] = max(f[j], f[j - v[i]] + w[i]);
            //cout<<f[j]<<' ';
        }
        //cout<<endl;
    }            
    cout << f[m] << endl;
    return 0;
}

完全背包与01背包的对比

//完全背包模板
	//遍历物品数,当前遍历到的是第i件物品
    for (int i = 1; i <= n; i ++ ){
    	//遍历背包容量(背包能装下的体积)(从小到大遍历)
        for (int j = v[i]; j <= m; j ++ ){//[v[i],m]
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }            
    cout << f[m] << endl;
//01背包模板
//遍历物品数,当前遍历到的是第i件物品
    for (int i = 1; i <= n; i ++ ){
        //遍历背包容量(背包能装下的体积)(从大到小遍历)
        for (int j = m; j >= v[i]; j -- ){//[m,v[i]]
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    cout << f[m] << endl;

模板对比:从小到大是完全背包,从大到小是01背包,其他的一模一样!
注:多重背包也是从大到小

背包问题遍历背包容量的顺序
01背包从大到小 for (int j = m; j >= v[i]; j – )
完全背包从小到大 for (int j = v[i]; j <= m; j ++ )
多重背包从大到小 for(int j = n ; j >= 0 ;j–)

也就是说:只有完全背包是从大到小 巧记:“小浣熊”

在这里插入图片描述

备注:

//多重背包模板
//遍历每件物品
    for(int i = 0 ; i <= m ;i++)//从0开始还是从1开始,要看具体的题目
    {
        //遍历容量
        for(int j = n ; j >= 0 ;j--)
        {
            //遍历个数
            for(int k = 1 ; k *v[i] <= j && k <= s[i] ;k++)
            {
                //容量为j的方案数
                f[j] = max(f[j] , f[j-k*v[i]] + k*w[i]);
            }
        }
    }
    cout<<f[n]<<endl;//容量为n的方案数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踏过山河,踏过海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值