目录
在DFS方法(暴力)的基础上加入记忆化存储 版:可以减少很多重复计算
定义:
动态规划(Dynamic Programming,DP)是一种用于解决具有重叠子问题和最优子结构性质的问题的算法技术。 它通过将复杂问题分解为更简单的子问题,并利用子问题的解来构建原问题的解,从而避免重复计算,提高效率。
首先 动态规划就是有记忆性的递推
记忆化搜索 = 暴力dfs + 记录答案
学习路线(入门思路):
dfs暴力 ---> 记忆化搜索 --> 递推
对于简单一维DP理解和描述
例题:
以一道简单的跳台阶问题为例进入今天主题
题目:一个楼梯共有 n级台阶,每次可以走一级或两级,问从第 0级台阶走到第 n 级台阶一共有多少种方案。
思路:可以看出是斐波那契数列(看不出可以多写几项发现规律:第n个台阶只能从第n-1个跳一个台阶和第n-2个跳两个台阶获得,所以是f(n + 1) = f(n) + f(n - 1))
斐波那契数列的定义是 f(n + 1) = f(n) + f(n - 1)
DFS方法(暴力) 版
//DFS方法(暴力)
#include<stdio.h>
int res=0;
int dfs(int n)
{
if(n<=1)return 1; //递归边界
else
{
return dfs(n-1)+dfs(n-2); //递归式
}
}
int main()
{
int n;
scanf("%d",&n);
res=dfs(n);
printf("%d",res);
return 0;
}
在DFS方法(暴力)的基础上加入记忆化存储 版:可以减少很多重复计算
// 在DFS方法(暴力)的基础上加入记忆化存储
#include<stdio.h>
int res=0;
int st[200]; //st[n]储存 到达第n台阶需要多少步
int dfs(int n)
{
if(st[n]) //如果想要的已经有了,就直接输出
{
return st[n];
}
else //没有就计算出来 st[n]= f(n) = f(n - 1) + f(n - 2)
{
return st[n]=dfs(n-1)+dfs(n-2);
}
}
int main()
{
int n;
scanf("%d",&n);
st[0]=1;
st[1]=1;
res=dfs(n); //res==st[n]是成立的
printf("%d",st[n]); //=printf("%d",res);
return 0;
}
将上面代码(记忆化存储版代码)进行规范化
//将上面代码(记忆化存储版代码)进行规范化
#include<stdio.h>
int main()
{
int i,n,st[20];
scanf("%d",&n);
st[1] = 1;
st[2] = 2;
for (i = 3; i <= n; i++)
{
st[i] = st[i - 1] + st[i - 2];
}
printf("%d",st[n]);
return 0;
}
动态规划版(节省空间版)
//动态规划版(节省空间版)
#include<stdio.h>
int main()
{
int i,n;
int temp1,temp2,temp3;
scanf("%d",&n);
temp1 = 1;
temp2 = 2;
//状态转移方程
for (i = 3; i <= n; i++)
{
temp3 = temp1 + temp2;
temp1=temp2;
temp2=temp3;
}
if(n==1)printf("%d",temp1);
else if(n==2)printf("%d",temp2);
else printf("%d",temp3);
return 0;
}
分析:
其中:斐波那契数列性质 f(n + 1) = f(n) + f(n - 1)为转移方程
动态规划的重点是写出 状态转移方程(递推公式)
动态规划最核心的思想,就在于拆分子问题,记住过往,减少重复计算
1**dfs > 2**记忆化搜索 > 3**逆序递推 >4**顺序递推** > 5**优化空间 !
由上面的例子可以看出:
递推 的公式 = dfs 向下 递归 的公式
递推 数组的初始值 = 递归 的边界
dfs转化为数组 ,dfs得到的结果也都存储在数组里,就可以做到大量剪枝(也就是完成了记忆化搜索)
优化空间:因为有些数组用完里面的数据之后就没有用处了,所以可以换成 动态变量(不断刷新变量)
本文只是对动态规划的初步理解 ------ 一维简单DP,希望可以与大家相互讨论学习。