动态规划问题的基本思想:
(1)动态规划问题的关键在于正确地写出基本的递推关系式和恰当的边界条件(也就是基本方程)。
要做到这一点,必须将问题的过程划分成几个相互联系的阶段,选取恰当的状态变量,决策变量以及定义最优值函数,从而把一个大问题化成一族同类型的子问题 , 然后逐个求解。即从边界条件开始 , 逐段递推寻优 , 在每一个子问题的求解中 , 均利用了它前面的子问题的最优化结果 , 依次进行 , 最后一个子问题所得的最优解 , 就是整个问题的最优解。
(2)在多阶段决策的过程中,动态规划方法是既把当前一段和未来各段分开,又把当前效益和未来效益结合起来考虑的一种最优化方法。因此,每段决策的选取是从全局来考虑的。与该段的最优选择答案一般不同。
(3)在求整个问题的最优策略时,由于初始状态时已知的,而每段的策略都是该段状态的函数,故最优策略所经过的各段状态便可以逐次变换得到,从而确定了最优路线。
上面的解法称为逆序解法,如果我们把G点作为起点,A点作为终点,则得到了顺序解法(思想一样,不做赘述)。但是动态规划的求解方向都与行进方向是相反的。
可以用动态规划法解题的特征:
动态规划求解问题的四个特征:
①求一个问题的最优解;
②整体的问题的最优解是依赖于各个子问题的最优解;
③小问题之间还有相互重叠的更小的子问题;
④从上往下分析问题,从下往上求解问题;
在明确了动态规划的基本概念和基本思想之后 , 我们看到 , 给一个实际问题建立动态规划模型时 , 必须做到下面五点 :
- (1) 将问题的过程划分成恰当的阶段 ;
- (2) 正确选择状态变量 sk , 使它既能描述过程的演变 , 又要满足无后效性 ;
- (3) 确定决策变量 uk 及每阶段的允许决策集合 D k ( sk ) ;
- (4) 正确写出状态转移方程 ;
- (5) 正确写出指标函数 V k,n , 它应满足下面三个性质 : ① 是定义在全过程和所有后部子过程上的数量函数 ;② 要具有可分离性 , 并满足递推关系。 即Vk,n( sk , uk , … , sn+1 ) = ψ k [ sk , uk , Vk+1,n(sk+1,uk+1 , … , sn+1 ) ];③ 函数 ψ k ( sk , uk , Vk+1 , n ) 对于变量 Vk+1, n 要严格单调。
以上五点是构造动态规划模型的基础 , 是正确写出动态规划基本方程的基本要素。
比如,斐波那契数列数列,将fib(7)=fib(6)+fib(5),fib(6)=fib(5)+fib(4),计算fib(6)时所用到的fib(5)不用再计算一遍,可以调用之前计算好的fib(5).
将fib(6)的过程追随到fib(1),中间的值都算出来了,复杂度为O(n)。分治法的情况下为O(2^n)。
实例:加权项目时间规划
求最大值
方法:选或不选
OPT(i)指当我考虑第i个项目的最优解。当i=8时,
如果选第8个,那么收益为4+OPT(5),因为6和7都与8冲突。不选,OPT(8)=OPT(7)。
我要在这两个之中选一个最好的。
pre(i)为必须选第i个的情况下,前面能做的最大的项目
事先算出pre(i)
这与斐波那契数列的展开式相似。出现了重叠子问题。
先算出OPT(1).再依次算出OPT(2)-OPT(8)。记录下每个对应的所选的项目。结果如下:
C++代码
#include<bits/stdc++.h>
using namespace std;
struct project{
int v1,v2,value;
project(int a,int b,int c):v1(a),v2(b),value(c){
}
};
int main(){
int n;
scanf("%d",&n);
int m = n;
int opt[n+1]={0};
int prev[n+1]={0};
vector<project>time;
while(n--){
int a,b,c;
printf("%d zhb\n",n);
scanf("%d%d%d",&a,&b,&c);
time.push_back(project(a,b,c));
}
for(auto i:time){
printf("v1 %d v2 %d value %d",i.v1,i.v2,i.value);
}
int k = 8;
for(auto j = time.end()-1;j!=time.begin()&&k>=2;--j,--k){
for(auto m = j-1;m>=time.begin();--m){
if(m->v2<=j->v1){
prev[k] = int(m-time.begin())+1;
printf("prev[%d] %d ",k,prev[k]);
break;
}
}
printf("\n");
}
for(int i =1;i<=m;++i){
printf("prevlast %d\n",prev[i]);
}
for(int i =1;i<=m;++i){
for(int j=1;j<i;++j){
}
opt[i]=max(opt[i-1],time[i-1].value+opt[prev[i]]);
printf("opt[%d] %d ",i,opt[i]);
printf("\n");
}
//for(int i = 1;i<=m;++i){
// printf("opt[%d] %d",i,opt[i]);
//}
printf("%d\n",opt[m]);
return 0;
}
//输入:8
// 1 4 5
// 3 5 2
// 0 6 8
// 4 7 4
// 3 8 6
// 5 9 3
// 6 10 2
// 8 11 4
//输出:12
OJ每日一练试水第5天,聊聊动态规划的问题
来源:CSDN
作者:Cainell
链接:https://blog.csdn.net/Cainell/article/details/103270236