什么是动态规划
定义:
把多阶段问题转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法(多阶段就是看这个问题是不是一层一层的),(优化问题通常就是求最大值、最小值、方法数等等)
阶段:
把问题分成几个相互联系的有顺序的几个环节,这些环节即为阶段(简单来说就是一层一层的)
状态:
某一阶段的出发位置称为状态,通常一个阶段包含若干状态(上一个阶段的结束位置,通俗点来说,就是现在走到哪了,我是谁,我在哪)
决策:
从某阶段的一个状态演变到下一个阶段某状态的选择(我从哪里来,我到哪里去)
策略:
由开始到终点的全过程中,每段决策组成的决策序列称为全过程策略(结果是怎么来的)
状态转移方程:
前一阶段的终点就是后一阶段的起点,前一阶段的决策选择导出了后一阶段的状态,这种关系描述了由i阶段到i+1阶段状态的演变规律,称为状态转移方程。
形如:
f[i] = f[i - 1]+ f[i - 2]
f[i,j]=max(f[i-1,j],f[i-1,j-1])+a[i,j]
等等
什么时候会使用动态规划(即适用条件)
- ——具有相同子问题
• 首先,我们必须要保证这个问题能够分解出几个子问题,并且能够通过这些子问题来解决这个问题。
• 其次,将这些子问题做为一个新的问题,它也能分解成为相同的子问题进行求解。
• 也就是说,假设我们一个问题被分解为了A,B,C三个部分,那么这A,B,C分别也能被分解为A’,B’,C’三个部分,而不能是D,E,F三个部分。
- ——满足最优子结构
• 问题的最优解包含着它的子问题的最优解。即不管前面的策略如何,此后的决策必须是基于当前状态(由上一次决策产生)的最优决策
(也就是具有状态转移方程)
- ——满足无后效性
• “过去的步骤只能通过当前状态影响未来的发展,当前的状态是历史的总结”。这条特征说明动态规划只适用于解决当前决策与过去状态无关的问题状态,出现在策略任何一个位置,它的地位相同,都可实施同样策略,这就是无后效性的内涵.
• 这是动态规划中极为重要的一点,**如果当前问题的具体决策,会对解决其它未来的问题产生影响,**如果产生影响,就无法保证决策的最优性
动态规划的原理
分类加法原理:
做一件事,完成它可以有n类办法,在第一类办法中有m1种不同的方法,在第二类办法中有m2种不同的方法,……,在第n类办法中有mn种不同的方法,那么完成这件事共有N=m1+m2+m3+…+mn种不同方法。
分步乘法原理:
做一件事,完成它需要分成n个步骤,做第一步有 m1种不同的方法,做第二步有 m2种不同的方法, ……,做第n n步有 mn种不同的方法,那么完成这件事共有N=m1× m2× m3× …× mn种不同的方法。
怎么做动态规划相关问题
First ,结合原问题和子问题确定状态:(我是谁?我在哪?)
• 题目在求什么?要求出这个值我们需要知道什么?什么是影响答案的因素?
• (一维描述不完就二维,二维不行就三维四维。)
• 状态的参数一般有
• 1)描述位置的:前(后)i单位,第i到第j单位,坐标为(i,j),第i个之前(后)且必须取第i个等
• 2)描述数量的:取i个,不超过i个,至少i个等
• 3)描述对后有影响的:状态压缩的,一些特殊的性质
Second, 确定转移方程:(我从哪里来?/ / 我到哪里去?)
• 1)检查参数是否足够;
• 2)分情况:最后一次操作的方式,取不取,怎么样取——前一项是什么
• 3)初始边界是什么。
• 4)注意无后效性。比如说,求A就要求B,求B就要求C,而求C就要求A,这就
不符合无后效性了。
根据状态 枚举最后一次决策 (即当前状态怎么来的)
就可确定出状态转移方程!
Third, , 考虑需不需优化
Forth, , 确定编程实现方式
简单动态规划经典例题
- 递推
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll dp[50];
int main(){
int n,m;
scanf("%d",&n);
while(n--){
scanf("%d",&m);
memset(dp,0,sizeof(dp));
dp[1] = 0;//确定边界,进行初始化
dp[2] = 1;
dp[3] = 2;
for(int i = 4;i <= 40;i++){
dp[i] = dp[i - 1] + dp[i - 2];//状态转移方程
}
printf("%lld\n",dp[m]);
}
return 0;
}
- 区间dp
一、 啥是区间dp:
区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法
二、 板子:
for(int len = 1;len <= n;len++){//枚举长度
for(int j = 1;j + len - 1<= n;j++){//枚举起点
int end = j + len - 1;
for(int i = j;i < end;i++){//枚举分割点
dp[j][end] = min(dp[j][end],dp[j][i] + dp[i + 1][end] + something);
//是不是dp[j][i] + dp[i+1][end]需要具体情况具体分析,左右端点有可能重合
}
}
}
题意:
给一个字符串包含( )[ ]四种 需要求最大能匹配的括号数
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[105][105];
int main(){
while(1){
string s;
cin >> s;
memset(dp,0,sizeof(dp));
if(s == "end") break;
int len = s.length();
for(int i = 1;i <= len;i++){
for(int j = 0;j + i - 1 < len;j++){
int ends = j + i - 1;
if((s[j]=='('&&s[ends]==')')||(s[j]=='['&&s[ends]==']')){
dp[j][ends] = dp[j + 1][ends - 1] + 2;
}//具体情况具体分析
for(int k = j;k < ends;k++){
dp[j][ends]=max(dp[j][ends],dp[j][k]+dp[k+1][ends]);
}
}
}
printf("%d\n",dp[0][len - 1]);
}
return 0;
}
补充
重复计算相同子问题,代码效率低下