动态规划的主要思想就是把之前计算过的储存起来,之后就直接调用,不重复计算,来节约时间,提高效率。
以Fibonacci数列的计算为例子:
public int fib(int n)
{
if(n<=0) return 0;
if(n==1) return 1;
return fib(n-1) + fib(n-2);
}
递归树如上所示,当计算fib(5)时候会计算fib(4)和fib(3),而右侧计算fib(4)时候还需要重复计算,如果把已经计算过的结构储存起来,再次使用的时候直接调用结果会提高计算效率。
【1】自顶向下的备忘录方法
public static int Fibonacci(int n)
{
if(n<=0) return n;
int []Memo=new int[n+1];
for(int i=0;i<=n;i++)
Memo[i]=-1;
return fib(n, Memo);
}
public static int fib(int n,int []Memo)
{
//如果已经求出了fib(n)的值直接返回,否则将求出的值保存在Memo备忘录中。
if(Memo[n]!=-1)
return Memo[n];
if(n<=2) Memo[n]=1;
else Memo[n]=fib( n-1,Memo)+fib(n-2,Memo);
return Memo[n];
}
建立一个数组来存放已经计算出来的结果,如果对应的数组不为-1,直接返回该数组对应的结果,如果没有计算过,计算后储存在数组中。
【2】自底向上的动态规划
动态规划的核心,先计算子问题,再由子问题计算父问题
public static int fib(int n)
{
if(n<=0)
return n;
int []Memo=new int[n+1];
Memo[0]=0;
Memo[1]=1;
for(int i=2;i<=n;i++)
{
Memo[i]=Memo[i-1]+Memo[i-2];
}
return Memo[n];
}