基础的动态规划问题结构与思路
基础的动态规划就是以线性结构和01背包问题的原型所构造的一些问题,并且会以此衍生出各种奇奇怪怪复杂的问题,这里通过一些题目来加深自己的理解和认识
原理
这里的原理分为两个小类:
- 线性序列(如LCS,LIS)
- 01背包及其变形
线性序列
线性序列的基本结构实际上就是序列,这里动态规划所操作的对象实际上就是序列,状态表示有很多变形,但基本的思路其实是表示一段长度序列的性质。
例题 UVA-1626
题目大意:
规定以下规则为规则序列:
- 如果S是规则序列,则(S),[S]为规则序列
- 空序列为规则序列
- 如果A,B为规则序列,则AB也为规则序列
给定序列,尽量添加少的括号使得序列变为规则序列,并输出这个规则序列
这里只讨论动态转移方程是如何得到的
首先我们可以看出这道题的问题结构实际上就是一个线性的序列,所以我们可以将状态设计为区间(i,j)或者区间(1,i)中序列的性质。在这道题目中可以设计dp(i, j)表示序列中区间为(i,j)的规则序列所需的最小括号。 那么下一步我们考虑如何进行状态转移。实际上在线性序列中,状态转移可以简单的表示为从区间到区间的转移。这道题目中我们可以这样设计状态转移方程:
dp(i, j) = dp(i+1,j -1) //if ( str i ==str j )
dp(i, j)=min(dp(i, j), dp(i, k)+dp(k+1, j) ) (i<=k&&k<=j)
那么下面就是考虑编程的问题了
for(int r=1;r<n;r++){
//第一层循环枚举的是序列的长度
for(int i=1;i<=n-r;i++){
//第二层循环枚举的是起点
int j=i+r;
dp[i][j]=n;//因为(i,j)的最大长度为n,那么初始化的时候就可以
//将它设为n
if(str[i]=='('&&str[j]==')'||str[i]=='['&&str[i]==']'){
dp[i][j]=dp[i+1][j-1];
path[i][j]=-1;//记录i,j区间插入的点在哪里
}
for(int k=i;k<=j;k++){
//k循环表示的是中间的序列
if(dp[i][j]>dp[i][k]+dp[k+1][j]){
dp[i][j]=dp[i][k]+dp[k+1][j];
path[i][j]=k;
}
}
}
}
所以我们通过这一道例题的简单分析,我们可以初步知道解决线性序列问题所需要注意的地方。
01背包
01背包的基本模型是简单的,但是我们需要进一步抽象化这一类型的题目。01背包的问题背景是给定物品的序列,每个物品都有一定的体积和价值,选择物品使得容量为V的背包装的物品价值最大化。
实际上这个题目可以进一步抽象为集合里的每一个元素都有一定的费用与价值,求不超过一定的费用的价值最大的子集。背包9讲里面涉及到的这些问题实际上归根结底是01背包的一部分条件的抽象化。这里面的问题实际上把动态规划与搜索中的关系探究的特别彻底,每一个细节都照顾的非常到位。这里我用动态规划的常见写法把这些类型归结一下
define the dp array //在这里会出现二维的背包问题,也就是说会消耗两种
//属性的价值,同时由于数组有可能会开的特别大所以
//会用一个属性的值来表示另一个属性的值
// such as cow exhibition这道题目
init the dp array //这里就会涉及到恰好,至少,至多为V的背包模型
for the ith subject //这里会涉及到分组背包的问题
//这里又会遇见枚举的顺序问题
//一定程度上这表明说是有很多不同角度去理解动态规划
for every posible cost //枚举费用的时候又会涉及到枚举的顺序问题
//比如降维数组
update the dp array// 在这里没有固定的方式,比如第K大价值的求法
// 就不仅仅需要把导致最大决策的值记录下来,还
// 需要把每一次的决策做到位
在update这一步与搜索有一定的关联,换句话说,以后学习动态规划的题解的时候还需要考虑有多少个选择(或者是说有多少状态会被影响到)。另外动态规划的写法也是非常重要的。qwq具体还是不太会。
第一次写博客,主要是加深对动态规划的理解
下次写一些动态规划题目题解的个人理解