动态规划:循环 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$$ 
这种时候,可能并不太好造成一个循环形式,可能就得递归实现。



相关文章
相关标签/搜索