背包问题dp

一、 单背包问题

  • 解法归纳:

    • 一、如果装不下当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一
      样的。

    • 二、如果装得下当前物品。

      • 假设1:装当前物品,在给当前物品预留了相应空间的情况下,前n-1个物品的最佳组合加上当前物品的价值就是总价值。

      • 假设2:不装当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。

      • 选取假设1和假设2中较大的价值,为当前最佳组合的价值。

  • 得到最大价值
//动态规划找到容量为r的背包所能装下的最大价值,n个物品
int maxValue(int n, int r) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= r; j++) {
            if (weight[i] > j)
                dp[i][j] = dp[i - 1][j];
            else
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        }
    }
    return dp[n][r];
}
  • 回溯寻找背包里的物品
//回溯找到背包里的物品是什么,i为编号,j为容量,n为物品数量,在object[]里存放
void fid(int i, int j, int n) {
    if (i == 0) {
        for (int k = 1; k <= n; k++)
            if (object[k] == 1)
                printf("%d ", k);
        return;
    }
    if (dp[i][j] == dp[i - 1][j]) {
        object[i] = 0;
        fid(i - 1, j, n);
    } else if (dp[i][j] == max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])) {
        object[i] = 1;
        fid(i - 1, j - weight[i], n);
    }
}

  • 全部代码
const int maxn = 1e5 + 6;
//weight-->weight,value-->value,dp[编号][容量]-->前n个编号的在容量之内的最优价值
int weight[maxn], value[maxn], dp[10000][10000];
int object[maxn];

//动态规划找到容量为r的背包所能装下的最大价值,n个物品
int maxValue(int n, int r) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= r; j++) {
            if (weight[i] > j)
                dp[i][j] = dp[i - 1][j];
            else
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        }
    }
    return dp[n][r];
}

//回溯找到背包里的物品是什么,i为编号,j为容量,n为物品数量,在object[]里存放
void fid(int i, int j, int n) {
    if (i == 0) {
        for (int k = 1; k <= n; k++)
            if (object[k] == 1)
                printf("%d ", k);
        return;
    }
    if (dp[i][j] == dp[i - 1][j]) {
        object[i] = 0;
        fid(i - 1, j, n);
    } else if (dp[i][j] == max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])) {
        object[i] = 1;
        fid(i - 1, j - weight[i], n);
    }
}


int main() {
    int n, r, w, v;
    scc(n, r);
    for (int i = 1; i <= n; i++) {
        scc(w, v);
        weight[i] = w;
        value[i] = v;
    }
    p(maxValue(n, r));
    fid(n, r, n);
}

二、 多重背包问题

POJ-1276

问题概述:

背包容量bag,有num[i]个价值为value[i]、重量为weight[i]的物品,求最高价值

代码:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include <vector>
#include <cctype>
#include <cstdlib>

using namespace std;

#define ll long long
#define il inline
#define oo 2147000000
#define sc(x) scanf("%d",&x)
#define scc(x, y) scanf("%d%d",&x,&y)
#define sccc(x, y, z) scanf("%d%d%d",&x,&y,&z)
#define p(x) printf("%d\n",x)
#define m(x, y) (x+y)>>1
#define l(x) x<<1
#define r(x) x<<1|1
#define MAX 1000000

int dp[MAX];//存储最后背包最大能存多少
int value[MAX], weight[MAX], num[MAX];//分别存的是物品的价值,每一个的重量以及数量
int bag;

void zeroOnePack(int weight, int value) {//01背包
    int i;
    for (i = bag; i >= weight; i--) {
        dp[i] = max(dp[i], dp[i - weight] + value);
    }
}

void completePack(int weight, int value) {//完全背包
    int i;
    for (i = weight; i <= bag; i++) {
        dp[i] = max(dp[i], dp[i - weight] + value);
    }
}

void multiplePack(int weight, int value, int num) {//多重背包
    if (bag <= num * weight) {//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包
        completePack(weight, value);
        return;
    } else {//否则就将多重背包转化为01背包
        int k = 1;
        while (k <= num) {
            zeroOnePack(k * weight, k * value);
            num = num - k;
            k = 2 * k;//这里采用二进制思想
        }
        zeroOnePack(num * weight, num * value);
    }
}

int main() {
    int n;
    while (~scc(bag, n)) {
        int i, sum = 0;
        for (i = 0; i < n; i++) {
            scc(num[i],value[i]);//此题没有weight,可以认为weight=value
        }
        memset(dp, 0, sizeof(dp));
        for (i = 0; i < n; i++) {
            //调用多重背包,注意传参的时候分别是重量,价值和数量
            multiplePack(value[i], value[i], num[i]);
        }
        p(dp[bag]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值