0
点赞
收藏
分享

微信扫一扫

信息学赛培 | 13 动态规划题型分析与实战


导读

信息学能够有助于孩子未来工作发展,提升孩子的综合能力。


上一节课,我们讲了动态规划的基本理论,并做了简单的练习,这节课,我们深入讲解动态规划题型,讲解题目特点,并通过实例讲解做题方法。


1 动态规划回顾

首先我们先来复习一下动态规划吧。


动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。


动态规划有如下重要概念:


1、阶段
2、阶段变量
3、状态
4、无后效性
5、决策
6、策略
7、最优策略
8、最优化原理
9、状态转移方程


动态规划局限性如下:


没有统一的处理方法,难以应用
存在维数障碍


2 动态规划答题方法

我们先看一下答题方法,后面通过实际的题目实战一下!

1 动态规划题目类型

动态规划题型千奇百怪,但是总体来说,总体有如下几种:




线性动态规划
区间动态规划
树形动态规划
背包动态规划


线性动态规划是指目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值。


区间动态规划的状态表示一般为d[i][j],表示区间[i, j]上的最优解,然后通过状态转移计算出[i+1, j]或者[i, j+1]上的最优解,逐步扩大区间的范围,最终求得[1, len]的最优解。区间动态规划也被拓展为表示索引为i的项和索引为j的项之间的最优解。例如将i分为j份。从将1分为1份一直推导到将n分为m份。


树形动态规划是指在树结构上做动态规划。树本身就具有子结构,非常容易和动态规划结合。但是树形动态规划一般也要和递归算法那结合。所以树形动态规划一般较难。


背包动态规划是非常经典的动态规划。背包动态规划一般也叫背包问题,这类问题的特点是有一个上界,并且每一种情况不可分。如果使用贪心难以达到全局最优的情况。需要使用动态规划去考虑。


除此之外,我们还有状态压缩动态规划动态规划优化问题等。

2 动态规划题目特点

题目要满足如下几个特点才可以使用动态规划:


重叠子问题
最优子结构
无后效性


我们上节课有讲过最优子结构和无后效性的概念,这里我们说一下重叠子问题。


前面我们的例子中,例如我们找15分为1,5和11,最好情况运算的过程中,我们在计算后面的时候,都会用到前面的情况。例如f[10]是f[12]的子问题,也是f[13]的子问题。

3 动态规划步骤

动态规划题目,我们虽然没有固定的解题方法,但是通用的步骤还是有的。


一般来说,动态规划题目有如下几个步骤:


1、明确子结构是什么
2、明确子结构怎么表示
3、明确子结构间的关系
4、明确子结构范围


在我们前面的引入的例子中,每个宝藏的重量就是子结构。然后我们使用数组来表示子结构。然后我们定义了状态转移方程,就是子结构之间的关系。我们上界是15,这个就是我们子结构的范围。


其中最重要的是第三步,也就是确定状态转移方程,具体原因我们会在下面的题目中讲解。


3 动态规划经典例题

本节课的作业,就是复习上面的所有知识,并完成下面两道题目!

1 分苹果

小明现在有一框苹果,一共有n个,现在要把这框苹果分为m堆,每堆至少有一个苹果。小明想知道一共有多少种分法,所以过来找你帮忙。要求任意两种分法不能相同。


例如:小明有10个苹果,要分3堆,下面这几种分法算作一种:


2,2,6;
2,6,2;
6,2,2;


【分析】


这道题目是非常经典的区间动态规划。




我们可以使用枚举,遍历所有情况,并且要求后面的数据要大于等于前面的数据,这样枚举也是可以的。


例如,将10个分成3份:


1 1 8
1 2 7
1 3 6
1 4 5
2 2 6
2 3 5
2 4 4
3 3 4


但是因为具体的份数没有,所以使用循环实现枚举不现实,可以使用递归。


但是这道题目更合适的方式是使用动态规划。



其实一道题目中,找到子结构以及子结构的表示方法不是最重要的,最重要的是我们要找到能够达成具有递推关系的状态转移方程。只要能找到这个方程,方程中满足递推关系并构成状态转移方程的每一个部分就是子结构。一般我们都用数组来描述。


这样,我们就解决了前三个部分,最后一个部分就是边界,也是终止条件。在我们这道题目中,我们要将n个苹果分成m堆,那么n和m就是边界。


所以我们把重心放在状态转移方程上面,状态转移方程也是动态规划的难点。


我们看一下将10个苹果分3堆,我们可以分两种情况:


至少有一堆有一个苹果
每一堆都至少有两个苹果


对于第一种情况,我们可以默认第一堆为一个苹果,剩下的两堆任意。那这样的情况,和我们把9个苹果分为后两堆是一样的。


对于第二种情况,我们可以让每一堆都减少一个苹果。这样依然能够保证每一堆有苹果。这样的情况,和我们把7个苹果分为3堆是一样的。我们给每一堆再各加一个苹果就是将10个苹果分成3堆并且保证每一堆至少有两个苹果。


所以我们有如下公式:


将10个苹果分3堆的情况 = 将9个苹果分2堆的情况 + 将7个苹果分3堆的情况


这就是我们的状态转移方程,子结构为将n个苹果分为m堆的情况数。我们用二维数组来表示子结构:


dp[n][m]:表示将n个苹果分为m堆


所以上述状态转移方程可以写为:


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


边界如下:


1<=i<=n;
1<=j<=min(i,m)


然后我们就可以遍历所有情况了。


我们要注意,对于dp[i][1]来说:


dp[i][1] = dp[i-1][0] + dp[i-1][1]


dp[i-1][0]要为0,然后dp[i-1][1]会一直计算到dp[1][1]:


dp[1][1] = 1


在我们的代码中,我们使用如下方式:


if (i==1&j==1) dp[i][j]=1;


一方面,这样我们循环就可以按照我们的边界开始,只需要给dp[1][1]单独赋值。另一方面,位运算的运算效率要高于逻辑运算。所以使用位运算做判断。


这道题目,有了上面的就非常简单了,我们直接给出代码:


#include<iostream>
using namespace std;
int n,m,dp[20005][20005];
int main(){
cin>>n>>m;
for (int i=1;i<=n;++i)
for (int j=1;j<=i&&j<=m;++j){
dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
if (i==1&j==1) dp[i][j]=1;
}
cout<<dp[n][m]<<endl;
return 0;
}


我们也可以给f[i][1]单独赋值。


f[i][1] = 1


然后边界从2开始即可。


执行结果如下:


信息学赛培 | 13 动态规划题型分析与实战_动态规划


2 0/1背包

一个旅行者有一个最多能用m公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn.若每种物品只有一件求旅行者能获得最大总价值。


【输入格式】   
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。


【输出格式】
仅一行,一个数,表示最大总价值。


【样例输入】
10 4
2 1
3 3
4 5
7 9
【样例输出】
12


【分析】


因为每一个物品都只有一个,所以我们应该从物品的角度去遍历。考虑他们之间的组合是否是满足在背包重量之内,并且价值最大。如果比原有价值大,那么我们就可以替换原有的价值。


当我们要把第i个物品放入,如果能放入,那么:


当前最大价值 = max(新放入后的最大价值,之前的最大价值)


假如我们用f[j]来表示背包容量为j时的最大价值。w[i]表示物品i的重量,c[i]表示物品i的价值,那么:


f[j] = max(f[j - w[i]] + c[j], f[j])


这是时候,我们就要考虑边界,因为物品只有n个,所以i的边界就是n。背包的容量为m,所以背包的边界为m。


我们从放入不同的物品角度考虑。我们考虑每放进去一个物品,容量就会减少物品的重量,但是价值就会加上对应物品的价值。所以我们需要每次遍历物品的时候,都要去更新放入不同物品的价值。


代码如下:


#include<iostream>
using namespace std;

const int maxm = 2001, maxn = 31;
int m, n;
int w[maxn], c[maxn];
int f[maxm];
int main(){
cin>>m>>n;
for (int i=1; i <= n; i++)
cin>>w[i]>>c[i];

for (int i=1; i <= n; i++)
for (int j = m; j >= w[i]; j--)
if (f[j-w[i]]+c[i]>f[j])
f[j] = f[j-w[i]]+c[i];
cout<<f[m];
return 0;
}


执行结果如下:


信息学赛培 | 13 动态规划题型分析与实战_动态规划_02


4 作业

本节课的作业,就是复习上面的所有知识,并完成下面的题目!

1 完全背包

设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大;


【输入格式】   
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。


【输出格式】
仅一行,一个数,表示最大总价值。


【样例输入】
12 4
2 1
3 3
4 5
7 9
【样例输出】
15




AI与区块链技术

信息学赛培 | 13 动态规划题型分析与实战_动态规划_03


举报

相关推荐

0 条评论