个人关于背包问题的总结(一)

一.前言

背包问题是动态规划的一个巨大的分支,常见的背包问题都有相对的模版,个人认为如果只是会背板子是下下之策,从长远的角度来看是不可取的,因此我想在这里分享一些个人对于背包问题的理解(会有借鉴其他大牛地方,逃~)同时如果我有一些不正的确的地方也欢迎大家和我交流。希望能加深大家对背包问题的理解,

二.01背包问题理解以及常见的例题

1.01背包的分析以及理解

动态规划(dp)问题的一般求解步骤概括如下

1.首先设计状态

2.设计状态转移方程



OK,切入正题,先看一道经典的01背包问题(2. 01背包问题 - AcWing题库):

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

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

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i件物品的体积和价值。

输出格式

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

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8

1.题目理解以及样例分析

顾名思义:01背包就是对于一个物品要么不拿(0)要么不拿(1)且不超过体积限制求其最大值

对于上述样例我们可以发现对于物品2,3我们选择取(1),而对于物品1,4我们选择不取(0)从而得到最大价值。

2.状态分析以及状态转移方程的确定(借鉴闫氏dp分析法)

状态分析

那么这种题目我们要考虑什么状态从而进行状态转移呢?01背包问题通常考虑在前i个物品中体积为j的背包所能取到的最大价值的集合,而我们所想要得到的是在前N个物品中体积为总体积V的背包所能取到的最大价值,他是包含于前i个物品中体积为j的背包所能取到的最大价值的集合中的,而在这个集合中也许会有其他几种不同状态的最大价值与答案相等,但是这并不会影响我们得出最后的答案,也就是说重复不会影响最终的答案,只要该集合不漏,或者多出其他不存在的情况;

转移方程

那么如何推导状态转移方程呢?根据上述的分析我们就要结合分析每个集合的关系以及题目的特征

01背包问题最大的特征是:要么拿(转移方式1)并获得对应的价值,要么不拿(转移方式2);

其中转移方式1还有体积的限制,因为背包是有限的。

因此可以得到伪代码如下

        分析第i  (i<=N)个物品的取舍情况

                背包的容量为j   (j<=V)时对于第i个物品的取舍情况

                        如果此时背包的容量大于v[i]可以考虑取还是不取

                        否则就不能取第i个物品

根据伪代码得到核心代码如下

         for(int i=1;i<= N;i++)

                     for(int j=1;j<=V;j++)

                        if(j>=v[i])   f[i][j]=max(f[i-1][j],  f[i-1][j-v[i]]+w[i]);

                        (如果不取的话f[i][j]==f[i-1][j],取的话就相当于f[i-1][j-v[i]]的基础上加上物品的价值)

                        else f[i][j]=f[i-1][j];

代码优化1

然而还没有结束我们可以根据数组的更新方式对代码做进一步优化以进一步节省空间就样例而言,

其更新的方式如下图:

    

我们可以发现for循环更新的方式是一层一层更新的,从第1层更新到第N层实际上我们最后所求的答案是由第N-1层的数据得到的,因此如果我们将数组写成一维数组的话就保留第二维状态即背包的容积(用f[j]表示背包容积为j时能够装物品的最大价值),此时只需要调整代码第二层循环使其从大到小更新,那么就能确保我们用来更新的数据就是i-1层的数据,如果不调整用的就是第i层的数据就会出错。               

代码优化2

根据优化1的优化原理可知我们用来更新的数组是第i-1层的数组,那么对应在代码第二层循环中j<=v[i]的情况,我们可以直接省略,因为第i-1层的f[i]不能够被更新相当于默认其不变;

代码第二层循环可以优化为:for(int j=V;j>=v[i];j--)f[j]=max(f[j],f[j-v[i]]+w[i]);

01背包问题的常见例题及其分析

例题一:采药P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1048

 题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”


如果你是辰辰,你能完成这个任务吗?

 输入格式

第一行有 2 个整数  T(1≤T≤1000) 和 M(1≤M≤100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。

接下来的  M  行每行包括两个在 1 到 100 之间(包括 1 和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

## 输出格式

输出在规定的时间内可以采到的草药的最大总价值。

 样例 

### 样例输入 #1
70 3
71 100
69 1
1 2

### 样例输出 #1

3

## 提示

**【数据范围】**

  • 对于 30%的数据,M≤10;
  • 对于全部的数据,M≤100。

**【题目来源】**

NOIP 2005 普及组第三题

题目分析:

将背包问题中的体积(做某件事情要消耗的东西)和价值(贡献)分别对应到本题中的采某株药对应的时间和某株药的价值,简单的模版题;

ac代码:

例题二:装箱问题(简单的变形)1024. 装箱问题 - AcWing题库

题目描述:

有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数)。

要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入格式

第一行是一个整数 V,表示箱子容量。

第二行是一个整数 n,表示物品数。

接下来 n 行,每行一个正整数(不超过10000),分别表示这 n 个物品的各自体积。

输出格式

一个整数,表示箱子剩余空间。数据范围

0<V≤200000,
0<n≤30,

输入样例:
24
6
8
3
12
7
9
7
输出样例:
0
 题目分析:

这道题目从01背包的角度来分析的话有两个理解上的小难点:1.如何从01背包的角度去理解题目提出的问题,题目问最小剩余空间实际上求最大能装多少货物,然后用总体积减去即可;2.题目只给出了货物的体积而没有给出价值,其实在这里相对于题目的要求来说货物的体积就等于其对于剩余空间贡献的价值。

ac代码:

三、小结

        这篇博客总结我对01背包原理的一些看法以及一些典型的题目,只考察01背包比较简单,我们更应该将01背包的思想融入到其他类型背包问题的思考中,这是比较困难的。

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-01-28 18:02:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-28 18:02:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-28 18:02:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-28 18:02:01       18 阅读

热门阅读

  1. HTML — 样式 CSS

    2024-01-28 18:02:01       31 阅读
  2. C#中常见的软件设计模式及应用场景

    2024-01-28 18:02:01       26 阅读
  3. 【GitHub项目推荐--区块链项目】【转载】

    2024-01-28 18:02:01       35 阅读
  4. Python版本管理和虚拟环境

    2024-01-28 18:02:01       34 阅读
  5. 使用 Optional 优雅处理可能为null的值

    2024-01-28 18:02:01       33 阅读
  6. Hive之set参数大全-18

    2024-01-28 18:02:01       26 阅读
  7. (四)SQL函数

    2024-01-28 18:02:01       33 阅读