算法学习(二)——动态规划初步
参考:
算法-动态规划 Dynamic Programming–从菜鸟到老鸟
动态规划(Dynamic Programming)基础
这个小例子其实已经可以阐述动态规划的核心了:记住已经解决过的子问题的解
小例题:斐契那波数列问题
相信大家都知道美国电影里的FBI,可是大家是否知道意大利数学家斐波那契提出的著名的FIB数列呢,其定义如下:对于正整数数列A[n]有
A [ 1 ] = 1 , A [ 2 ] = 1 , A [ N ] = A [ N − 1 ] + A [ N − 2 ] ( N > 2 ) A[1] = 1, A[2] = 1, A[N] = A[N-1] + A[N-2] (N>2) A[1]=1,A[2]=1,A[N]=A[N−1]+A[N−2](N>2)
现在你的任务是求出Fibonacci数列的第n项。
这个题我记得是刚学数组的时候的一道练习题,当然,这个题用递归其实就是几行代码的事情,超级简单,但是今天我们要来看看递归和简单DP的差距。
/*递归算法*/
public static int recursionFibonacci(int n){
if(n==0)
return 0;
if(n==1)
return 1;
return recursionFibonacci(n-1)+recursionFibonacci(n-2);
}
借用一下大佬博客上面的图,我们来看看用简单递归来求这个数列的算法效率。
这里可以看到,假如我们输入6,然后这是使用简单递归算法的步骤,可以看到,光是fib(2)就执行了5次,这不就很浪费时间了。
下面我们看看使用动态规划的算法:
//自顶向下的备忘录法
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)
{
if(Memo[n]!=-1)
return Memo[n];
//如果已经求出了fib(n)的值直接返回,否则将求出的值保存在Memo备忘录中。
if(n<=2)
Memo[n]=1;
else Memo[n]=fib( n-1,Memo)+fib(n-2,Memo);
return Memo[n];
}
//自底向上的动态规划
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];
}
这里展示了两种动态规划的算法示例,代码不一一赘述,只要记住动态规划的核心是记住已经解决的子问题的过程然后再看代码就一目了然了。
实操
PowerOJ 1027:买花瓶(简单动态规划DP)
Description
john想买几个花瓶装饰一下新房。已知花店的花瓶被固定地排成一行并被依次排号,且花店有
一个原则,花瓶不能移动也不能挑选,只能买连号的花瓶。由于每个花瓶都有自己的美学价值,
请你帮john选出其中美学价值最大的一组。
比如,有六个花瓶,它们的美学价值分别是 3 -1 2 4 -6 5,那么john应该选3 -1 2 4(美学价值为8)
这一组。而不能挑2 3 4 5这一组(违犯),或是2 4组(美学价值为6,不是最大)。
Input
测试有很多次,对于每一次测试:
第一行为一个正整数 N( 1 <= N <=10000 ),代表花瓶的个数.当N=0时,代表测试结束;
第二行的N个整数Mi( -100 <= Mi <= 100 )分别代表每个花瓶的美学价值,其间用空格分开。
output
每次测试输出一行,代表拥有最大美学价值的一组连号花瓶的美学价值MAX。
Sample Input
6
3 -1 2 4 -6 5
0
Sample Output
6
解题
public static int maxAC(int array[],int n){
int result=array[0];//保存最大值
for (int i=1;i<n;i++){
if (array[i-1]>0){
array[i]=array[i-1]+array[i];
} else {
array[i]=array[i];
}
}
for (int i=1;i<n;i++){
if (array[i]>result)
result=array[i];
}
return result;
}