动态规划之背包问题

本文详细介绍了动态规划在解决01背包、完全背包、多重背包和分组背包问题中的应用。通过状态转移方程,展示了如何优化空间复杂度,以更高效地求解最大价值。对于多重背包问题,提出了朴素和二进制优化两种解决方案。
摘要由CSDN通过智能技术生成

动态规划之背包问题


背包模型

Dp{
    状态表示{
        集合{
            表示的是所有选法
                
            满足条件条件{
                1.只从前i个物品中选
                
                2.总体积<=j
            }
        }
            
        属性  Min,Max,数量
    }
    
    状态计算 集合划分
}
01背包问题

n个物品和一个容量为v的背包,每一个物品体积vi,价值wi,每件物品只能用一次.

求背包能装的下的情况下能装下的最大价值为多少

我们考虑递推式,即状态转移方程。考虑第i件物品选或不选取决于选了之后手否能使价值变大;假设 我们已经选了第i-1件物品,那么我们第i件物品的选择便是:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])

(对于第i件物品,背包容量为j时,不选的值为dp[ i-1,j ] ,选的话为dp[i-1,j-v[i]]+w[i] ,此处的状态都在前面的状态一致的情况下得到)

再观察一下式子发现i-1甚至有点多余,因此我们呢可以对递推式做空间优化,即将dp数组优化成一维的:

for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--){
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

完全背包问题

每件物品可以用无限次,只要装得下。

状态转移方程代码:

for(int i=1;i<=n;i++)
	for(int j=0;j<=m;j++)
		for(int k=0;k*v[i]<=m;k++)
			dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*v[i]]+k*w[i])

我们考虑对其做优化,参考01背包,先对空间做优化,然后我们可以推导出
d p [ j ] = m a x ( d p [ j ] , d p [ j − v [ i ] ] + w [ i ] , d p [ j − 2 v [ i ] ] + 2 w [ i ] . . . ) dp[j]=max(dp[j],dp[j-v[i]]+w[i],dp[j-2v[i]]+2w[i]...) dp[j]=max(dp[j],dp[jv[i]]+w[i],dp[j2v[i]]+2w[i]...)

d p [ j − v [ i ] ] = m a x ( d p [ j − v [ i ] ] , d p [ j − 2 v [ i ] ] + w [ i ] . . . ) dp[j-v[i]]=max(dp[j-v[i]],dp[j-2v[i]]+w[i]...) dp[jv[i]]=max(dp[jv[i]],dp[j2v[i]]+w[i]...)

对比上两个方程我们可以推导出:
d p [ j ] = m a x ( d p [ j ] , d p [ j − v [ i ] ] + w [ i ] ) dp[j]=max(dp[j],dp[j-v[i]]+w[i]) dp[j]=max(dp[j],dp[jv[i]]+w[i])

实现代码:

for(int i=1;i<=n;i++)
    for(int j=0;j<=m;j++)
        dp[j]=max(dp[j],dp[j-v[i]]+w[i])

多重背包问 题

每件物品的数量有限制,各为 Si:

1.朴素版

​ 核心思想:二进制优化;

​ 我们将s[i]拆分成二进制表示的1,2…2k,其中2k<=s[i]/2

​ 然后转化成01背包来做
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − k ⋅ v [ i ] ] + k ⋅ w [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-k\cdot v[i]]+k\cdot w[i]) dp[i][j]=max(dp[i1][j],dp[i1][jkv[i]]+kw[i])
​ 其中k=0,1,2…s[i]

我们将dp数组的行进行压缩可以得到:
d p [ j ] = m a x ( d p [ j ] , d p [ j − k ⋅ v [ i ] ] + k ⋅ w [ i ] ) dp[j]=max(dp[j],dp[j-k\cdot v[i]]+k\cdot w[i]) dp[j]=max(dp[j],dp[jkv[i]]+kw[i])

实现代码:

for(int i=1;i<=n;i++)
    for(int j=m;j>=0;j--)
        for(int k=0;k<=s[i];k++)
            dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i])
2.二进制优化版

我们可以将每个物品的数量s做二进制优化,即将每一个s拆分成1,2,4…2k,s+1-2(k+1)。这样我们可以使用这些数表示任意1-s区间内的每一个数,并且时间复杂度也可以从原来的O(nms)优化到O(nmlogs)。然后我们呢对于拆分出来的物品用01背包的思路来做即可;

代码:

for(int i=1;i<=n;i++){  //二进制优化
        int a,b,c;
        cin >> a >> b >> c;
        int u=1;
        while(c>=u){
            v[++cnt]=a*u;
            w[cnt]=b*u;
            c-=u;
            u*=2;
        }
        if(c>0){
            v[++cnt]=a*c;
            w[cnt]=b*c;
        }
    }
    n=cnt;
    
    for(int i=1;i<=cnt;i++)  //01背包
        for(int j=m;j>=v[i];j--){
            dp[j]=max(dp[j],dp[j=v[i]]+w[i]);
            cout << dp[m] << endl;
        }

分组背包问题

物品有n组,每一组有若干个,每一组最多选一个,求最大价值
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 , j − v [ i ] [ k ] ] + w [ i ] [ k ] ) dp[i][j]=max(dp[i-1][j],dp[i-1,j-v[i][k]]+w[i][k]) dp[i][j]=max(dp[i1][j],dp[i1,jv[i][k]]+w[i][k])
因此本质上还是01背包问题,我们将其转化为01背包,只不过多一层循环k依次迭代更新每一组的各件物品;

for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            for(int k=0;k<=s[i];k++)
                if(j>=v[i][k])dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值