动态规划——完全背包问题

问题的描述

完全背包问题

N N N种物品和一个容量是 V V V的背包,每种物品都有无限件可用。
i i i种物品的体积是 v i v_i vi,价值是 w i w_i wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

数据具体内容

输入格式

第一行两个整数, N N N V V V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N N N行,每行两个整数 v i , w i v_i,w_i vi,wi,用空格隔开,分别表示第 i i i种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0 < N , V ≤ 1000 0<N,V≤1000 0<N,V1000
0 < v i , w i ≤ 1000 0<vi_,w_i≤1000 0<vi,wi1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

10

动态规划的朴素做法

代码

//三维代码
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int m, n;
int v[N], w[N];
int f[N][N];

int main()
{
    // get inputting
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> v[i] >> w[i];
    }
    
    // Dynamic programming
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= m; j++)
        {
            for(int k = 0; k * v[i] <= j; k++)
            {
                f[i][j] = max(f[i][j], f[i-1][j - k * v[i]] + k * w[i]);
            }
        }
    }
    
    // output
    cout << f[n][m] << endl;
    return 0;
}

解题逻辑梳理

  1. 同01背包问题不同的是,完全背包问题中每个物品的数量是不限的,因此对物品 i i i,想拿多少就拿多少。因此不能将 f ( i , j ) 和 f ( i − 1 , j ) f(i,j) 和 f(i-1, j) f(i,j)f(i1,j)之间的关系简单的分为,拿第 i i i件物品和不拿第 i i i件物品了,而是要对拿 0 − n 0-n 0n件物品 i i i都逐一进行分析。
  2. f ( i , j ) f(i,j) f(i,j)依旧代表在有 i i i种物品,背包空间为 j j j的情况下,所有的可行解的物品总价值的集合。
  3. 按照01背包问题的情况分类规则,对完全背包问题也采用相同的分析方法。
    • 只考虑是否拿第 i i i个物品的情况,拿了不同个数的 i i i种类物品,关系式应该如何表示。其情况可以分为,分别拿了 i i i种物品 0 , 1 , 2 , . . . , n 0,1,2,...,n 0,1,2,...,n件进入背包。那么就有关系式 f ( i , j ) = { f ( i − 1 , j − k ∗ v [ i ] ) + k ∗ w [ i ] ∣ k = 0 , 1 , 2 , . . . , n } f(i, j) = \{f(i-1, j-k*v[i]) +k*w[i]|k=0,1,2,...,n\} f(i,j)={f(i1,jkv[i])+kw[i]k=0,1,2,...,n}
    • 由于每种物品都是无限多个,最少情况下是拿 0 0 0个,最多则可以一直拿,拿到背包装不下为止,因此拿取物品的个数 k k k(整数)必须满足 k > = 0 k >= 0 k>=0 j > = k ∗ v [ i ] j >= k * v[i] j>=kv[i]两个条件。
    • f ( i , j ) f(i, j) f(i,j)是所有可行方案的集合,题中所求的是具有最大总价值的选择方法的最大总价值是多少,即 m a x ( f ( i , j ) ) max(f(i,j)) max(f(i,j))

二维动态规划

代码

//二维解答
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int m, n;
int v[N], w[N];
int f[N][N];

int main()
{
    // get inputting
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> v[i] >> w[i];
    }
    
    // Dynamic programming
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= m; j++)
        {
            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]);
        }
    }
    
    // output
    cout << f[n][m] << endl;
    return 0;
}

解题逻辑梳理

  1. 二维的代码是在朴素做法的基础上得到的,因此基本思想还是朴素做法不变,只是在朴素做法的基础上作等价代换,使得表示上更加简单。
  2. 在朴素做法中,有关系式
    f ( i , j ) = { f ( i − 1 , j − k ∗ v [ i ] ) + k ∗ w [ i ]   ∣   k = 0 , 1 , 2 , . . . , n } f(i, j) = \{f(i-1, j-k*v[i]) +k*w[i]~|~k=0,1,2,...,n\} f(i,j)={f(i1,jkv[i])+kw[i]  k=0,1,2,...,n},因此可以推导出 f ( i , j − v [ i ] ) = { f ( i − 1 , j − k ∗ v [ i ] ) + ( k − 1 ) ∗ w [ i ]   ∣   k = 1 , 2 , 3 , . . . , n } f(i, j-v[i]) = \{f(i-1, j-k*v[i]) + (k-1)*w[i]~|~k = 1, 2, 3, ..., n\} f(i,jv[i])={f(i1,jkv[i])+(k1)w[i]  k=1,2,3,...,n}
    那么可以看出 f ( i , j ) f(i,j) f(i,j)除了 k = 0 k=0 k=0时候的表达式,剩下的 k = 1 , 2 , 3 , . . . , n k=1,2,3,...,n k=1,2,3,...,n时候的余项等于 f ( i , j − v [ i ] ) + w [ i ] f(i,j-v[i]) + w[i] f(i,jv[i])+w[i]
    因此我们就得到了一套新的关系式,即:
    f ( i , j ) = { f ( i − 1 , j ) , f ( i , j − v [ i ] ) + w [ i ] } f(i,j) = \{f(i-1,j), f(i, j - v[i]) +w[i]\} f(i,j)={f(i1,j),f(i,jv[i])+w[i]}
  3. 可以看到在这个过程中,k消失了,因此就将k的循环删去,成功实现了降维。但是,我们可以明显发现,在降维的公式中, j j j必须大于 v [ i ] v[i] v[i]的时候,二维关系式才成立。因此需要对 j j j在小于和大于 v [ i ] v[i] v[i]时候分情况讨论。

一维动态规划

代码

//一个维修改
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int m, n;
int v[N], w[N];
int f[N];

int main()
{
    // get inputting
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> v[i] >> w[i];
    }
    
    // Dynamic programming
    for(int i = 1; i <= n; i++)
    {
        for(int j = v[i]; j <= m; j++)
        {
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    
    // output
    cout << f[m] << endl;
    return 0;
}

解题逻辑梳理

  1. 从二维到一维的过程实际和01背包问题从二维向一维转换有着异曲同工之妙,但是也有一定的差异。二维到一维的优化也是建立在等价变换的基础上的。
  2. 首先尝试对代码减少一个维度,因为同01背包问题类似,在考虑完全背包问题时,也是只考虑了对前 i i i种物品和前 i − 1 i-1 i1种物品考虑选择方案得到的结果之间的关系。因此,也选择消去 i i i这个维度。
  3. 由于 f [ j ] = m a x ( f ( j , j − v [ i ] ) + w [ i ] ) f[j] = max(f(j, j - v[i]) + w[i]) f[j]=max(f(j,jv[i])+w[i]),可以知道需先求出 f ( i , j − v [ i ] ) f(i,j-v[i]) f(i,jv[i]),而后根据 f ( i , j − v [ i ] ) f(i,j-v[i]) f(i,jv[i])求算 f ( i , j ) f(i,j) f(i,j)。因此, j j j是依次增大的循环。并且由于 j > = v [ i ] j >= v[i] j>=v[i],所以 j j j v [ i ] v[i] v[i]开始循环,到最大值结束。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值