动态规划

动态规划是一种非常精妙的算法思想,它没有固定的写法,极其灵活,常常需要具体问题具体分析。(整理自《算法笔记》)

  1. 什么是动态规划:动态规划是一种用来解决一类最优问题的算法思想。简单来说,动态规划是将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。需要注意的是,动态规划会将每个求解过的子问题的解记录下来,这样当下一次碰到同样的子问题时,就可以直接使用之前记录的结果,而不是重复计算。
  2. 一般使用递归或者递推的写法来实现动态规划,其中递归写法在此处又称为记忆化搜索。
  3. 动态规划的递归写法:

以斐波那契数列为例。

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

实际上,这个递归涉及了许多重复的计算。为了避免重复计算,可以开一个一维数组 dp。其中dp[n]记录F(n)的结果,并用dp[n]=-1表示F(n)没有被计算过。

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-2 )//计算F(n),并保存至dp[n]
        	return dp[n];  //返回F(n)的结果
        }
}

通过记忆化搜索,降低了复杂度。
通过上述例子可以引申一个概念:如果一个问题可以被分解为若干个子问题,且这些子问题会重复出现,那么就称这个问题拥有重叠子问题。一个问题必须拥有重叠子问题,才能使用动态规划去解决。

  1. 动态规划的递推写法
    典型的数塔问题为例(如图):最后将路径上所有数字相加后得到的和最大是多少?动态规划_第1张图片

    令dp[i][j]表示从第i行和第j个数字出发的到达最底层的所有路径中能得到的最大和。

如 dp[1][1]就是从位置(1,1)到达最底层的最大和。

用式子表示:dp[i][j] = max (dp[i+1][j],dp[i+1][j+1])+f[1][1]
把dp[i][j]称为问题的状态,而把上面的式子称为状态转移方程。数塔的最后一层的dp值总是等于元素本身,即dp[n][j]==f(n)(j) (1<=j<=n),这种可以直接确定其结果的部分称为边界。这样就可以从最底层各位置的dp值开始,不断往上求出每一层各位置的dp值,最后就会得到dp[1][1]。

根据这种思想写出动态规划的代码:

#include
#include
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. 最优子结构:一个问题的最优解可以由其子问题的最优解有效的构造出来。需要指出,一个问题必须拥有最优子结构,才能使用动态规划去解决。
  3. 分治与动态规划:分治和动态规划都是将问题分解为子问题,然后合并子问题的解得到原问题的解。但分治法分解出的子问题是不重叠的。分治法解决的问题不一定是最优化问题,而动态规划解决的问题一定是最优化问题。
  4. 贪心与动态规划:贪心和动态规划都要求原问题必须拥有最优子结构。如上例的数塔,贪心法就是从最上层开始,每次选择左下和右下两个数字中较大的一个,一直到最底层得到最后结果,显然不一定得到最优解。

你可能感兴趣的:(算法)