DP-背包问题

01背包

问题:一共有N件物品,第i(i从1开始)件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?
分析
1、二维状态数组
采用暴力的形式,定义二维数组表示状态

dp[i][j] 表示将前i件物品放入背包限重为j时所获的最大价值( 0 <= i <= n, 1 <= j <= w);

状态方程,有两种情况:
(1)不装入第i件物品:dp[i][j] = dp[i - 1][j];
(2)选择第i件物品:dp[i][j] = dp[i - 1][j - w[i]] + v[i];

dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // j >= w[i]

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;

#define LENGTH 105
#define VAL 1005

int main()
{
    int n, w;
    scanf("%d %d", &n, &w);
    int weight[LENGTH] = {0};
    int val[LENGTH] = {0};
    for(int i = 0; i < n; i ++){
        scanf("%d %d", &weight[i], &val[i]);
    }
    int flag[LENGTH][VAL] = {0};
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= w; j ++){
            if(j >= weight[i - 1]){
                flag[i][j] = max(flag[i - 1][j], flag[i - 1][j - weight[i - 1]] + val[i - 1]);
            }else{
                flag[i][j] = flag[i - 1][j];
            }
        }
    }
    printf("%d\n", flag[n][w]);
}

2、一维状态数组
二维状态数组中,dp[i][j]状态值只需要考虑到dp[i-1][0……]时的状态,考虑去掉第一维,这样就可以使用一维的状态方程。

dp[j]表示限重为j时所获的最大价值(1 <= j <= v);

状态方程两种情况:
(1)不装入第i件物品:dp[j] = dp[j];
(2)装入第i件物品:dp[j] = dp[j - w[i]] + v[i];

dp[j] = max(dp[j], dp[j−w[i]]+v[i]);
注意:i从0到n向后遍历;j从w向前遍历(每个物品只能选择依次,反向遍历保证前面的重量和价值不会影响后面的重量和价值,可以与完全背包问题做对比)

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;

#define LENGTH 105
#define VAL 1005

int main()
{
    int n, w;
    scanf("%d %d", &n, &w);
    int weight[LENGTH] = {0};
    int val[LENGTH] = {0};
    for(int i = 0; i < n; i ++){
        scanf("%d %d", &weight[i], &val[i]);
    }
    int flag[VAL] = {0};
    for(int i = 0; i < n; i ++){
        for(int j = w; j >= weight[i]; j --){
            flag[j] = max(flag[j], flag[j - weight[i]] + val[i]);
        }
    }
    printf("%d\n", flag[w]);
}

完全背包

问题:完全背包与01背包不同就是每种物品可以有无限多个:一共有N种物品,每种物品有无限多个,第i(i从1开始)种物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?
分析
1、二维状态数组
状态方程:
(1)第i件物品不放入背包:dp[i][j] = dp[i - 1][j]
(2)第i件物品放入背包:因为每种物品有无限个(但注意书包限重是有限的),所以此时不应该转移到dp[i−1][j−w[i]]而应该转移到dp[i][j−w[i]],即装入第i种物品后还可以再继续装入第i种物品。

dp[i][j] = max(dp[i−1][j], dp[i][j−w[i]]+v[i]) // j >= w[i]

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;

#define LENGTH 1005

int main()
{
    int n, w;
    scanf("%d %d", &n, &w);
    int weight[LENGTH] = {0};
    int val[LENGTH] = {0};
    for(int i = 1; i <= n; i ++){
        scanf("%d %d", &weight[i], &val[i]);
    }
    int flag[LENGTH][LENGTH] = {0};
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= w; j ++){
            if(j >= weight[i]){
                flag[i][j] = max(flag[i - 1][j], flag[i][j - weight[i]] + val[i]);
            }else{
                flag[i][j] = flag[i - 1][j];
            }
        }
    }
    printf("%d\n", flag[n][w]);
}

2、一维状态数组
二位状态数组中,max第二项是dp[i]而01背包是dp[i-1],即这里就是需要覆盖前面的而01背包需要避免覆盖。

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

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;

#define LENGTH 1005

int main()
{
    int n, w;
    scanf("%d %d", &n, &w);
    int weight[LENGTH] = {0};
    int val[LENGTH] = {0};
    for(int i = 0; i < n; i ++){
        scanf("%d %d", &weight[i], &val[i]);
    }
    int flag[LENGTH] = {0};
    for(int i = 0; i < LENGTH; i ++){
        flag[i] = 0;
    }
    for(int i = 0; i < n; i ++){
        for(int j = weight[i]; j <= w; j ++){
            flag[j] = max(flag[j], flag[j - weight[i]] + val[i]);
            //printf("%d   %d   %d", i , j, flag[j]);
        }
    }
    printf("%d\n", flag[w]);
}

3、考虑物体的数量
从装入第 i 种物品多少件的角度出发,01背包只有两种情况即取0件和取1件,而这里是取0件、1件、2件…直到超过限重(k > j/w[i]),所以状态转移方程为:
二维状态数组

k为装入第i种物品的件数,
k <= j/w[i] dp[i][j] = max{(dp[i-1][j − kw[i]] +kv[i]) for every k}

for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= w; j ++){
            if(j < weight[i]){
                dp[i][j] = dp[i - 1][j];
            }else{
                for(int k = 1; k <= j / weight[i]; k ++){
                    dp[i][j] = max(dp[i][j], max(dp[i - 1][j], dp[i-1][j - k*weight[i]] + k*val[i]));
                }
            } 
        }

    }

一维状态数组

dp[0,…,W] = 0 for i = 1,…,N
for j = W,…,w[i] // 必须逆向枚举!!!
for k = [0, 1,…, j/w[i]]
dp[j] = max(dp[j], dp[j−kw[i]]+kv[i])

多重背包

问题
分析
1、一维数组+物品数量
类似于完全背包问题,只不过物品数量需要考虑两种情况:
(1)物品实际数量:nums[i];
(2)重量为j时最多能放该物品的数量:j / weight[i]

min(nums[i], j / weight[i])

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;

#define LENGTH 2005

int main()
{
    int n, w;
    scanf("%d %d", &n, &w);
    int weight[LENGTH] = {0};
    int val[LENGTH] = {0};
    int nums[LENGTH] = {0};
    for(int i = 1; i <= n; i ++){
        scanf("%d %d %d", &weight[i], &val[i], &nums[i]);
    }
    int dp[LENGTH] = {0};
    for(int i = 1; i <= n; i ++){
        for(int j = w; j >= weight[i]; j --){
            for(int k = 0; k <= min(nums[i], j / weight[i]); k ++){
                dp[j] = max(dp[j], dp[j - k * weight[i]] + k * val[i]);
            }
        }
    }
    printf("%d\n", dp[w]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值