动态规划范式
var catch = [[],[]];
function someObscureFunction(a,b){
if(...) return ...;//先处理初始部分
var ret = catch[a][b];
if(ret) return ret
return catch[a][b] = 求解
}
练习1 通配符
* 长度大于等于0的字符串
? 任一字符串
例如:
he?p 匹配 help,heap,无法匹配helpp.
*p* 匹配papa
代码如下,这个算法的复杂度为O(n*n*n),n<=100 可以接受。
<script> function matchMemozied(W1,S1){ var cache = []; var W = W1.split(''),S = S1.split(''); function match(w,s){ var w1 = w,s1=s,ret; if(cache[w1]){ if(cache[w1][s1]){ return cache[w1][s1]; } }else{ cache[w1] = []; } while(s<S.length && w< W.length && (W[w] == '?' || W[w] == S[s])) { ++w; ++s; } if(w == W.length ) return cache[w1][s1] = s==S.length; if(W[w] == '*') { for(var i=0;i+s<=S.length;i++){ if(match(w1+1,s+i)){ return cache[w1][s1] = 1; } } } return cache[w1][s1] = 0; } if(match(0,0)){ console.log(S1); } } matchMemozied('he?p','heap'); matchMemozied('he?p','heapp'); matchMemozied('*p*','papa'); </script>
代码二,O(n*n)
<script> function matchMemozied(W1,S1){ var cache = []; var W = W1.split(''),S = S1.split(''); function match(w,s){ var w1 = w,s1=s,ret; if(cache[w1]){ if(cache[w1][s1]){ return cache[w1][s1]; } }else{ cache[w1] = []; } if(s<S.length && w< W.length && (W[w] == '?' || W[w] == S[s])) { return cache[w1][s1] = match(w+1,s+1); } if(w == W.length ) return cache[w1][s1] = s==S.length; if(W[w] == '*') { if(match(w+1,s) || (s<S.length && match(w,s+1))) { return cache[w1][s1] = 1; } } return cache[w1][s1] = 0; } if(match(0,0)){ console.log(S1); console.log(cache); } } matchMemozied('he?p','heap'); matchMemozied('he?p','heapp'); matchMemozied('*p*','papa'); </script>
典型优化问题
优化问题就是在多个答案中选择出最佳答案(最优解),动态规划法起初是从快速解决优化问题
利用动态规划法解题也是从穷举搜索法开始的
练习:三角形的最大路径问题
6
1 2
3 7 4
9 4 1 7
2 7 5 9 4
从6开始往下走,只能向下和右下走(此时不计算向右走的一步的所在的数字),最终到达底部,找出数值之和最大的路径。
首先用穷举搜索法
<script> var data = [[6], [4,5], [7,1,6], [3,5,5,2], [9,1,3,6,2]]; function path1(y,x,sum){ if(y == data.length - 1){ return sum + data[y][x]; } return Math.max(path1(y+1,x,sum+data[y][x]),path1(y+1,x+1,sum+data[y][x])); } var max = path1(0,0,0); console.log(max); </script>
这个事枚举所有的,文章还提到另一种思路,找最优子结构
<script> var j=0; function path2(y,x){ j++; if(y === data.length -1){ return data[y][x]; } return Math.max(path2(y+1,x),path2(y+1,x+1)) + data[y][x]; } console.time('path2'); var max2 = path2(0,0,0); console.timeEnd('path2'); console.log('max2:' + max); console.log('j:' + j); </script>
这两个方法都跑了2的n次方,现在加入动态规划范式,第一种解法加范式的话需要三维数组,而且实际上没有节省执行次数,所以就没试,下一题就涉及到修改输入值来方便动态规划。
<script> var k=0,cache =[]; for(var m = 0;m< data.length;m++){ cache[m] = []; } function path3(y,x){ k++; if(y === data.length -1){ return data[y][x]; } if(cache[y][x]){ return cache[y][x]; }else{ return cache[y][x] = Math.max(path3(y+1,x),path3(y+1,x+1)) + data[y][x]; } } console.time('path3'); var max3 = path3(0,0,0); console.timeEnd('path3'); console.log('max3:' + max); console.log('k:' + jk); </script>
从结果上看,执行次数明显减少,结果也是相同的,但是执行时间略有增加。。。下一题,呜呜
最长递增子序列问题
比如 1,2,4 是 1,5,2,4,7 的递增子序列
先上枚举搜索算法
<script> function list(A){ var ret = 0; for(var i = 0;i<A.length;i++){ var B = []; for(var j = i+1 ; j<A.length;j++){ if(A[i]<A[j]){ B.push(A[j]); } } ret = Math.max(ret,1 + list(B)); } return ret; } var c = 100,data =[]; while(--c>0){ data.push(Math.floor(Math.random()*20)) } console.log(data); console.time('list'); var result = list(data); console.timeEnd('list'); console.log(result); </script>
现在的输入是数组,不适合缓存定位,现改为数组的下标作为参数
<script> function list1(start){ var ret = 1; for(var i = start+1;i<data.length;i++){ if(data[start]<data[i]){ ret = Math.max(ret,1 + list1(i)); } } return ret; } console.time('list1'); var result = list1(0); console.timeEnd('list1'); console.log(result); </script>
现在加入动态规划,就是缓存啦
<script> function list2(start){ if(cache[start +1 ]){ return cache[start + 1 ]; } cache[start + 1 ] = 1; for(var i = start+1;i<data.length;i++){ if(start == -1 || data[start]<data[i]){ cache[start + 1 ] = Math.max(cache[start + 1 ],1 + list2(i)); } } return cache[start + 1 ]; } var cache = [],max=0; console.time('list2'); for(var k = 0;k<data.length;k++){ max = Math.max(max,list2(k)); } console.timeEnd('list2'); console.log(max); </script>
总得时间复杂度还是n*n 的,还有更少的,n*logn