什么是动态规划
个人理解是:将问题分解成相似的子过程或步骤,将大事化小,逐步求解的方式。
动态规划三个要素
- 最优子状态:将复杂问题简单化的过程,建模的第一步。比如计算N的斐波那契数值。可以先寻找N和N-1的关系:F(N)=F(N-1)+F(N-2); 这就最优子状态,将“大”问题往小了化解,个人认为这一步是最难的。
- 边界:问题最终可以通过动态规划来计算的关键,是最优子状态的最简单情况。比如斐波那契数列中的F(0)=1和F(1)=1;就是该问题的边界,是后续推导的起点。
- 状态转移公式:就是对上述两点的数学建模。斐波那契数列的公式就是他的状态转移公式
N = 0: F(0)=1
N = 1: F(1)=1
N > 1: F(N) = F(N-1) + F(N-2)
例子
举个今天从微信公众号“算法和数据结构”上的问题,我用Javascript实现了递归算法:
/**
问题:假设有N个金矿,M个矿工,每个金矿的产量为g[i],需要人工p[i],每个金矿必须足员才能开功,求最大开采量及开采的矿
动态规划法:
假设最大开采量公式为F(N,M);
最优子结构:找到N个矿和N-1个矿之间的关系
假设开采第N个矿,则最大开采量应该是Max(F(N-1,M-p[n])+g[n],F(N-1,M));
假设不开采第N个矿,则最大开采量应该是F(N-1,M);
边界:
当N=1时,如果p[0] > M,则最大开采量为g[0];
如果p[0] < M,则最大开采量为0;
状态转换公式:
p[n] > M : F(N-1,M);
p[n] < M : Max(F(N-1,M-p[n])+g[n],F(N-1,M));
**/
function findWayToDig(w,ar){
if(ar.length<1){
console.log("The init array is empty!!!");
}
if(ar.length==1){
if(ar[0].ppl > w ){
return 0;
}else{
return ar[0].amt;
}
}
var amt = ar[0].amt;
var ppl = ar[0].ppl;
var next = ar.slice(1);
if(ppl > w){
return findWayToDig(w,next);
}else{
return Math.max(amt + findWayToDig(w-ppl,next),findWayToDig(w,next));
}
}
(function(){
var ar =[{amt:500,ppl:5},
{amt:200,ppl:3},{
amt:300,ppl:4
},
{amt:350,ppl:3},
{amt:400,ppl:5}];
console.log(findWayToDig(10,ar));
})();
- 首先上述方法有优化空间,可以增加一个临时对象保存历史计算结果findWayToDig(w,next)。
- 其次还可以使用迭代法(正向推导)来实现,这部分有空补上。