2021-08-14

算法笔记(7):01背包问题与完全背包问题的内存优化

一般实现代码

  1. 01背包问题
const int max_n = 105;
const int max_m = 10005;

int n, m;//n种物品,总重不超过m
int dp[max_n][max_m];//dp[i][j]指从前i个物品中选,总重不超过j时的背包最大价值
int w[max_n], v[max_n];

void solve()
{
    for (int i = 1; i <= n;i++)
        for (int j = 1; j <= m;j++)
        {
            if(w[i]>j)
                dp[i][j] = dp[i - 1][j];
            else
            {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
            }
        }
    
}
  1. 完全背包问题
const int max_n = 105;
const int max_m = 10005;

int n, m;//n种物品,总重不超过m
int dp[max_n][max_m];//dp[i][j]指从前i种物品中选,总重不超过j时的背包最大价值
int w[max_n], v[max_n];

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

内存优化

  1. 01背包问题
    在编写背包问题的代码中,我们注意到dp数组大小为n*m,占用了很多内存,故我们可以重复使用一维数组来减少内存消耗。
    直接理解这个过程并不简单,所以最好从二维数组的思路出发,再过渡到一维。
  • 首先从动态规划的状态转移方程开始思考

w [ i ] > j , d p [ i ] [ j ] = d p [ i − 1 ] [ j ] w[ i ] > j, dp[ i ][ j ] = dp[ i-1 ][ j ] w[i]>j,dp[i][j]=dp[i1][j];
w [ i ] < = j , d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] ) w[ i ] <= j, dp[ i ][ j ] = max(dp[ i - 1 ][ j ], dp[ i - 1 ][ j - w[ i ] ]+v[ i ]) w[i]<=j,dp[i][j]=max(dp[i1][j],dp[i1][jw[i]]+v[i]).

我们注意到,第i层的dp状态完全是由i-1层所决定的,与[1,i-2]没有关系,而递推过程又是不断向后进行的,即[1,i-2]层的递推结果已经没有作用了,由此可以想到,用一个一维数组来存放i-1层的结果,在第i层重复使用这个数组就可以了。
这时就存在一个问题,即我们递推的方向,再次观察状态转移方程,当我们递推 d p [ i ] [ j ] dp[i][j] dp[i][j] 时,要用到 d p [ i − 1 ] [ j − w [ i ] ] dp[i-1][j-w[i]] dp[i1][jw[i]] 的值,这里的 j − w [ i ] j-w[i] jw[i] 是在 j j j 的前面的,如果从前向后递推的话,前面的值会被新值覆盖,显然会造成错误结果,故我们应从后往前递推,下面给出代码。

const int max_n = 105;
const int max_m = 10005;

int n, m;             //n种物品,总重不超过m
int w[max_n], v[max_n];
int dp[max_m];

void solve()
{
    for (int i = 1; i <= n;i++)
        for (int j = m; j >= 1;j--)
        {
            if(w[i]>j)
            {
                dp[j] = dp[j];
            }
            else
            {
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
            }
        }
}
  1. 完全背包问题
    与上面同理,我们也考虑其状态转移方程

w [ i ] > j , d p [ i ] [ j ] = d p [ i − 1 ] [ j ] w[ i ] > j, dp[ i ][ j ] = dp[ i-1 ][ j ] w[i]>j,dp[i][j]=dp[i1][j];
w [ i ] < = j , d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w [ i ] ] + v [ i ] ) w[ i ] <= j, dp[ i ][ j ] = max(dp[ i - 1 ][ j ], dp[ i ][ j - w[ i ] ]+v[ i ]) w[i]<=j,dp[i][j]=max(dp[i1][j],dp[i][jw[i]]+v[i]).1

观察状态转移方程可得,第i层的状态与第i层与i-1层有关,并且递推 d p [ i ] [ j ] dp[i][j] dp[i][j] 时需要 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][jw[i]] 的值,故我们必须进行正向递推。下面给出代码。

const int max_n = 105;
const int max_m = 10005;

int n, m;             //n种物品,总重不超过m
int w[max_n], v[max_n];
int dp[max_m];

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

总结

关于这类问题的思考点应该从二维出发,先从二维考虑其状态转移方程,之后再推导到一维,相对而言便于理解很多。


  1. 这里解释一下这个方程, w [ i ] < = j w[i]<=j w[i]<=j时, d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ) ( k > = 0 & & k ∗ w [ i ] < = j ) dp[i][j]=max(dp[i-1][j-k*w[i]]+k*v[i])(k>=0\&\&k*w[i]<=j) dp[i][j]=max(dp[i1][jkw[i]]+kv[i])(k>=0&&kw[i]<=j),我们考虑 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][jw[i]],发现两者进行的循环比较大致相同,即 d p [ i ] [ j − w [ i ] ] = m a x ( d p [ i − 1 ] [ j − ( k + 1 ) ∗ w [ i ] ] + k ∗ v [ i ] ) ( k > = 0 & & ( k + 1 ) ∗ w [ i ] < = j ) dp[i][j-w[i]]=max(dp[i-1][j-(k+1)*w[i]]+k*v[i])(k>=0\&\&(k+1)*w[i]<=j) dp[i][jw[i]]=max(dp[i1][j(k+1)w[i]]+kv[i])(k>=0&&(k+1)w[i]<=j),将两个公式合并,我们便可以得到上面的公式。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值