动态规划之背包问题全解

动态规划是一种解决复杂问题的有效方法,由理查德·贝尔曼提出,通过存储子问题的解来避免重复计算。它适用于具有最优子结构、子问题重叠和无后效性的问题。文章介绍了动态规划的三要素:状态、阶段和决策,并通过01背包、完全背包和多重背包问题举例说明其应用,展示了如何通过状态转移矩阵求解最大价值问题。
摘要由CSDN通过智能技术生成

概述———动态规划

提出人:理查德·贝尔曼

本质:一张表格处理方法

内容:把原问题分解为若干子问题,自底向上先求解最小子问题,

    把结果储存在表格中,求解大的子问题时直接从表格中查询

    小的子问题的解,以避免重复计算,从而提高效率。

一、动态规划求解原理

适用范围:问题需要具备3个性质———最优子结构、子问题重叠、无后效性。

最优子结构指问题最优解包含其子问题的最优解,是使用动态规划的基本条件。

三要素:状态、阶段、决策

状态表示———将问题发展到各个阶段时所处的各种客观情况用不同的状态表示出来,确定状态和状态变量。状态的选择要满足无后效性。

阶段划分———按照问题的时特征或空间特征,将问题划分若干阶段。划分后的阶段一定是有序或可排序的,否则问题无法求解。

状态转移———根据上一个的状态和决策导出本阶段的状态。

边界条件———需要确定初始条件和边界条件

求解目标———确定问题的求解目标,根据状态转移方程的递推结果得到求解目标。

二、背包问题

背包问题:在一个有容积或重量限制的背包中放入物品,物品有体积、重量、价值等属性,要求在满足背包限制的情况下放置物品,使背包中物品的价值之和最大。

1.01背包

N个物品,每个物品都有重量Wi和价值Vi,每个物品只有一个(有——1,无——0)

背包容量W, 求解在不超过背包容量的情况下将哪些物品放入背包,才可以使背包中物品的价值之和最大;

状态表示:c[I][j]表示将前i种物品放入容量为j的背包中获得的最大价值。

j<w[i], c[i][j]=c[i-1][j]

j>=w[i], c[i][j] = max{c[i-1][j], c[i-1][j - W[i]] + V[i]}

Eg.

5个物品,背包容量10

重量:2,5,4,2,3

价值:6,3,5,4,6

求背包容量内物品价值和最大

c[][] 

012345678910
000000000000
100666666666
200666669999
30066661111111111
4006610101111151515
5006610121216161717
int n = 5;

int[] w = {2, 5, 4, 2, 3};

int[] v = {6,3,5,4,6};

int R = 10;

int[][] dp = new int[n+1][R+1];

for (int i = 1; i <= n; i++) {

    for (int j = 1; j <= R; j++) {

        if (j < w[i-1]) {

            dp[i][j] = dp[i-1][j];

        } else {

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

        }

    }

}

System.out.println(dp[n][R]);
// 最优解构造

int[] x = new int[n];

for (int i = 5; i > 0; i--) {

    if (dp[i-1][R] < dp[i][R]) {

        x[i-1] = 1;

        R-=w[i-1];

    } else {

        x[i-1] = 0;

    }

}

System.out.println(IntStream.range(0, n).filter(t -> x[t] == 1)

.map(t -> w[t]).boxed().collect(Collectors.toList()));

逆推算法优化(正推会有重复,数据覆盖引起)

int n = 5;

int[] w = {2, 5, 4, 2, 3};

int[] v = {6,3,5,4,6};

int R = 10;


int[] dp = new int[R+1];

for (int i = 0; i < n; i++) {

    for (int j = R; j >= w[i]; j--) {

        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);

    }

}

System.out.println(dp[R]);

2、完全背包

N个物品,每个物品都有重量Wi和价值Vi,其数量没有限制

背包容量W

求解在不超过背包容量的情况下将哪些物品放入背包,才可以使背包中物品的价值之和最大;

// 正推算法,从i阶段到i阶段

int n = 5;

int[] w = {1,2,3,4,5};

int[] v = {5,4,3,2,1};

int R = 10;



int[] dp = new int[R+1];

for (int i = 0; i < n; i++) {

    for (int j = w[i]; j <= R; j++) {

        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);

    }

}

System.out.println(dp[R]);

3、多重背包

N个物品,每个物品都有重量Wi和价值Vi,每种物品的数量都可以大于1但有数量限制

背包容量W,求解在不超过背包容量的情况下将哪些物品放入背包,才可以使背包中物品的价值之和最大;

(1)、暴力拆分(速度堪忧)

将第i种物品看作Ci种独立物品,每种物品只有一个———转成01背包问题

先找到所有物品,即先循环每种物品,再循环每种物品中每一个物品,最后逆序容量

(2)、二进制拆分

当c[I]*w[I]>=R,当完全背包问题

否则,采用二进制拆分,将c[i]个物品拆分成若干个新物品

存在最大整数P,使得2^0+2^1+·····+2^p<=C[i],剩余部分R[i]=c[i] - (2^0+2^1+·····+2^p);

(分成P+2堆原因需要思考下,是因为这这些能组合任意可能组合,如3=2^0+2^1等)

(3)、数组优化

用num[j]数组记录容量为j时放入了多少个i种物品,以满足物品数量限制。

完全背包+数组数量限制

4.分组背包(价值最大化)

给定n组物品,第i组有ci个物品,第i组的第j个物品有重量Wij和价值Vij背包容量为W,

在不超过背包容量的情况下每组最多选择一个物品,如何选择物品使价值和最大

将每个组看作一个物品,是为01背包问题,组内内部循环找最合适的物品

5.混合背包

01背包、完全背包、多重背包问题混合使用,具体问题具体判断

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值