动态规划:循环 vs 记忆化搜索.mdp

 
## 动态规划两种实现方式
根据状态转移,保留中间的计算结果,这种求解问题的算法叫做*动态规划(Dynamic Programming,DP)*,通过空间存取中间结果,避免中间结果的多次求解,从而节省程序的运行时间,是动态规划的主要特变。
 
典型的动态规划算法,实现方式有两种
 
记忆化搜索
自底向上的循环
 
### 记忆化搜索
实现方式类似递归,不过在具体求解前先判断是不是已经算过了,如果算过了,直接返回。
```cpp
int ans[M][N];
memset(ans, -1, sizeof(ans));
 
int solve(int m,int n){
     if(ans[m][n]==-1) {
        solve(m, n);
        return ans[m][n];
    }
    //judge whether current m,n is corner cases, if so, deal with it and return
    if(m<0||n<0) return XX;
    if(m==0 || n==0) {
        ans[m][n]=YY; 
        return ans[m][n;
    }
    //solve(m,n) according to recursive formula
    //set ans[m][n] to solved answer
    ans[m][n] = solve(m-1,n)+solve(m-2,n)+..+solve(m-p,n) +
                solve(m,n-1)+solve(m,n-2)+..+solve(m,n-q) +
                ...;
    return ans[m][n];     
}
 
```
 
* 优势:实现逻辑简单(类似递归),直接根据状态转移的逻辑,处理边界情况即可。
* 劣势:若某个状态计算时涉及的状态数较多,可能会造成栈溢出。
 
 
###自底向上的循环
从初始情况 -> 复杂的case -> 最终的结果, 由递推式自底向上。
比如,考虑下面的递推式
$$ f(m,n)=f(m-1,n)+f(m,n-1), 1\le m < M, 1\le n < N$$
$$ f(0,n)=f(m,0)=1$$
用自底向上的循环实现如下
```cpp
int solve(){
    int f[M][N];
    for(i=0; i<M;i++)
        f[i][0]=1;
    for(i=0; i<N;i++)
        f[0][i]=1;
    for(i=1;i<M;i++)
        for(j=1;j<N;j++)
            f[i][j] = f[i-1][j]+f[i][j-1];
    return f[M-1][N-1];
}
```
* 优势,循环实现,效率会比递归实现高些,且不会造成栈的溢出
* 劣势,某些情况下,如果状态转移过于复杂,可能不太容易写成清晰的循环形式
比如像这种
$$ f(n) = F (f(g(n))) , n\ne n_1,n_2,..n_k$$
$$ f(n_i)=p_i$$ 
这种时候,可能并不太好形成一个循环形式,可能就得递归实现。





posted on 2014-01-08 10:53  bian  阅读(366)  评论(0编辑  收藏  举报

导航