dp简介

动态规划的递归写法和递推写法

动态规划是一种算法思想,没有固定写法,十分灵活,需要具体问题具体分析

动态规划是一种用来解决一类最优化问题的算法思想。
即将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。
动态规划会将每个求解过的子问题的解记录下来,这样当下一次碰到同样的子问题时,就可以直接使用之前记录的结果。

动态规划的递归写法

以求解斐波那契数列为例
f0=1,f1=1,f(n)=f(n-1)+f(n-2)(n>=2)

int f(int n){
    if(n==0||n==1) return 1;
    else return f(n-1)+f(n-2);

}

上述代码就涉及到了很多重复计算,严重增大了时间复杂度
为了便重复计算,可以开一个dp数组,用来保存已经计算过的结果
其中dp[n]表示f(n)的结果

int dp[maxn];
int f(int n){
    if(n==0||n==1) return 1;
    if(dp[n]!=-1) return dp[n];
    else{
        dp[n] = f(n-1)+f(n-1);
        return dp[n];
    }
}

这种做法将复杂度由O(2^n)降到了O(n),这也是名词记忆化搜索的由来。

动态规划的递推写法

以数塔问题为例
从顶部出发在每一个节点可以选择向左或者向右走,一直走到底层,要求找出一条路径,
使得路径上的数字之和最大.
不妨令dp[i][j]表示从第i行第j个数字出发的到达底层的所有路径中能得到的最大和。
于是dp[1][1]就是dp[2][1]和dp[2][2]的较大值加上5
即: dp[1][1] = max(dp[2][1],dp[2][2]+f[1][1])

状态转移方程:
dp[i][j] = max(dp[i+1][j],dp[i+1][j+1]+f[i][j])

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1000;
int f[maxn][maxn],dp[maxn][maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
            scanf("%d",&f[i][j]);
        }
    }
    for(int j=1;j<=n;j++){
        dp[n][j] = f[n][j];
    }
    for(int i=n-1;i>=1;i--){
        for(int j=1;j<=i;j++){
            dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+f[i][j];
        }
    }
    printf("%d\n",dp[1][1]);
    return 0;
}

使用递推写法采用自底向上,即从边界开始,不断向上解决问题,直到解决目标问题
使用递归写法采用自顶向下,即从目标问题开始,将她分解成子问题的组合,直到分解至边界为止

  • 一个问题必须拥有重叠子问题和最优子结构,才能使用动态规划去解决

    1. 分治与动态规划:分治和动态规划都是讲问题分解成子问题,然后合并子问题的解得到原问题的解。
      但是分治法分解出的子问题时不重叠的,因此分治法解决的问题不拥有重叠子问题,而动态规划解决的问题拥有重叠子问题
      另外,分治法解决的问题不一定是最优化问题,而动态规划解决的一定是最优化问题

    2. 贪心和动态规划:贪心和动态规划都要求原问题必须拥有最优子结构。
      贪心并不等待子问题求解完毕后在选择使用哪一个,而是通过一种策略直接选择一个问题去求解,没被选择的子问题就
      不在去求解,直接抛弃。
      而动态规划不管是自底向上还是自顶向下,都是从边界开始向上得到目标问题的解。

猜你喜欢

转载自blog.csdn.net/hy971216/article/details/80292763
今日推荐