动态规划算法总结

**

动态规划算法总结

**

文章目录

    • 动态规划算法总结
  • 前言
  • 一、概念理解
  • 二、题目特点
  • 三、解题步骤
  • 四、例题练习
    • 1.硬币选择
    • 2.走方格问题
    • 3.青蛙跳石头问题
  • 五、总结提升


前言

动态规划的内容在各种算法比赛或大厂面试中占据的不少的部分,所以对动态规划算法的学习显得尤为重要。于是,在B站学习了九章算法的动态规划讲解,决定以此为基础对学习内容进行整理与总结


一、概念理解

我的理解为:将一个大的问题化成一个子问题,并将子问题化成更小的子问题,直到问题变成可以简单解决的问题。每一个子问题的输出结论,是更大问题的输入。
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。
对比于分治和递归,动态规划算法更快,有更高的效率。

二、题目特点

题目的特点也就是题目类型,有以下几种:
  1. 计数问题
  2. 最值问题
  3. 存在性问题

三、解题步骤

1.确定状态:
(1)开数组
(2)数组每个元素的含义
(3)两个意识:最后一步和子问题
2.转移方程
3.初始条件和边界情况
(1)初始条件:用转移方程算不出来的,需要手工定义
(2)注意不要数组越界
4.计算顺序
要计算F[x]时,右侧的状态必须已知

四、例题练习

1.硬币选择

动态规划算法总结_第1张图片
解题思路:

  • 可以知道无论怎样选择,总会有这样的情况:已经选择了n-1枚硬币,且为最少的情况,只需要挑选最后一枚硬币使硬币数最少。
  • 可以得到转移方程:F[x]=min{F[x-2]+1,F[x-5]+1,F[x-7]+1}
  • 初始条件:F[0]=0
  • 对于自变量为负的时候,将F的值设置为正无穷

代码如下:

#include 

#define N 27/*求解27块钱*/
#define MAX 0x3f3f3f3f/*表示正无穷*/

int min(int x,int y,int z)//求最小值
{
    int ret;
    if(x<=y)
    {
        if(x<=z) ret=x;
        else ret=z;
    }
    else{
        if(x<=z) ret=y;
        else{
            if(y<=z) ret=y;
            else ret=z;
        }
    }
    return ret;
}

int main()
{
    int i,f[N+1];//开数组
    f[0]=0;//初始条件
    for(i=1;i<N+1;i++)
    {
        /*如果越界,则赋值正无穷*/
        if(i-2<0) f[i-2]=MAX;
        if(i-5<0) f[i-5]=MAX;
        if(i-7<0) f[i-7]=MAX;
        /*利用转移方程进行求解*/
        f[i]=min(f[i-2]+1,f[i-5]+1,f[i-7]+1);//子问题
    }
    printf("%d",f[N]);
    return 0;
}

以上即为对最值型动态规划问题的求解过程

2.走方格问题


解题思路:

  • 首先可以知道在到达第(i,j)的格子(右下角)之前只有两种可能,即:在(i-1,j)和(i,j-1)的位置上
  • 所以得到转移方程为:F[i][j]=F[i-1][j]+F[i][j-1]
  • 初始条件为F[0][0]=1
  • 边界情况为:在i=0或者j=0的情况下都只有一种走法

代码如下:

#include 

/*7乘7的方格*/
#define M 7
#define N 7

int main()
{
    int f[M][N];//开数组
    int i,j;
    for(i=0;i<M;++i)//行
    {
        for(j=0;j<N;++j)//列
        {
            if(i==0||j==0) f[i][j]=1;//边界情况
            else f[i][j]=f[i-1][j]+f[i][j-1];//转移方程
        }
    }
    printf("%d",f[M-1][N-1]);
    return 0;
}

以上为对计数型动态规划问题的求解过程

3.青蛙跳石头问题


解题思路:

  • 首先青蛙在跳到第i个石头时,它先能跳到第j个石头上,且a[i]的值要大于等于j的值
  • 即转移方程为:F[i]=OR0(F[j] AND i+a[i]>=j)
  • 初始条件为:F[0]=1
  • 令True:1 False:0

代码如下:

#include 

#define N 100

int main()
{
    int i,j,n;
    int a[N];
    int f[N];
    /*输入数据*/
    scanf("%d",&n);
    for(i=0;i<n;i++) scanf("%d",&a[i]);
    /*计算(true:1  false:0)*/
    f[0]=1;//赋初值
    for(j=1;j<n;j++)
    {
        f[j]=0;//假设跳不了
        for(i=0;i<j;i++)
        {
            if(f[i]&&i+a[i]>=j)
            {
                f[j]=1;
                break;
            }
        }
    }
    if(f[n-1]==1) printf("True");
    else if(f[n-1]==0) printf("False");
    return 0;
}

以上为对存在型动态规划问题的求解过程

五、总结提升

  • 确定状态:(1) 研究最优策略的最后一步 (2) 将问题化为子问题
  • 转移方程:根据子问题的定义直接得到
  • 初始条件和边界情况:细心,考虑周全
  • 计算顺序:利用之前的计算结果

有问题欢迎各位大佬指出
算法系列将持续更新,欢迎关注,一起学习

你可能感兴趣的:(算法,c语言,算法,推荐算法,动态规划,数据结构)