动态规划范式

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 

 

posted on 2017-08-02 17:09  国足不快乐  阅读(116)  评论(0编辑  收藏  举报