一、递推思想
递推思想是动态规划的基础,我先以斐波那契数列为例来简单讲解一下编程中的递推思想。
- 斐波那契数列:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
斐波那契实际上就是这样的一个数列:1,1,2,3,5,8,13,21…(除了第一个第二个数以外其他的数是其前两个数的和)。
用代码来求这样一个递推式一般来说有两种方法:
- 递归法
//递归法
int Fibonacci(int n)
{
if (n == 1)
return 1;
else if (n == 2)
return 1;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);//返回前两个数的和
}
- 循环
//循环结构
int Fibonacci(int n)
{
if (n == 1)
return 1;
else if (n == 2)
return 1;
else
{
int num1 = 1, num2 = 1;
for (int i=2;i<n;++i)
{
int num = num2;
num2 = num2 + num1;//等于前两个数之和,将num2变为了函数的后一位数
num1 = num;
}
return num2;
}
}
//在一些情况下循环结构往往较为高效且安全,但实现过程较复杂
二、 斐波那契数列应用
在这里我再举一个有关斐波那契数列的简单应用
- 青蛙跳阶梯问题
一个青蛙一次只能跳一个阶梯或者两个阶梯,问这个青蛙跳上总阶梯数为n的楼梯总共有几种跳法?
在这个问题中,我们用f(n)来表示跳上n阶阶梯的跳法,当n>2时,青蛙在第一次跳跃时要么跳跃一个阶梯,要么跳跃两个阶梯,当跳跃一个时,那么青蛙还要接着跳跃n-1个阶梯(即f(n-1)种跳法),当跳跃两个时,青蛙还要接着跳跃n-2个阶梯(即f(n-2)种跳法)
所以我们得到了这样的一个递推式:
- f(n)=f(n-1)+f(n-2)(n>2)(f(1)=1,f(2)=2) ;
到这一步我们就将这个问题转换为了斐波那契数列问题。
三、动态规划基础
在讲完递推思想后,来看一个真正的动态规划题目:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额(题目来自leetcode,https://leetcode-cn.com/problems/house-robber)。
例:有数组[11,23,22]
在这个数组中要么选11和22,要么选23,显而易见选11和22和为33要大,输出结果为33。
我们以数组[11,23,34,24,26,27]为例来分析这个题目,在这个数组中一共有6个数,由于数组技术从0开始,这里用用f(5)来表示输出结果(小偷在这六家最多能偷的金额),第六个数为27。
(1)当小偷决定要偷第六家时,那么第五家就一定不能偷,只能接着在前四家接着偷,及在前四家决定偷窃方案,设前四家最多能偷的钱为f(3),我们发现此时最大金额为f(3)+27
(2)当小偷决定不偷第六家时,那么小偷只能在前五家选择他的偷窃方案,及f(4)。
(3)我们所求的f(5)应该为,f(5)=max( val(5)+f(3) , f(4) )(val(n)表示位置为n的这一家的金额,下同)
把这个规律应用到求解第n家,可以得到递推式:
- f(n)=max( val(n)+f(n-2) , f(n-1) (n>=2,f(0)=val(0),f(1)=max( val(0) , val(1) ) )
我们发现这个式子在实现过程上和斐波那契数列是十分相似的。
代码实现过程:
//nums表示这个数组
int rob(vector<int>& nums) {
if(nums.size()==0)//nums.size()表示数组的长度
{
return 0;
}
else if(nums.size()==1)
{
return nums[0];
}
else if(nums.size()==2)
{
return max(nums[0],nums[1]);
}
else
{
int optfirst = nums[0];//初始值设为f(0)
int optnext= max(nums[0],nums[1]);//初始值设为f(1)
for (int i = 0; i <nums.size()-2; ++i)
{
int num1 = optnext;//用num1表示f(i+1)
int num2 = optfirst + nums[i + 2];//用num2表示val(i)+f(i+2)
optfirst = optnext;//将optfirst变为f(i+1)
optnext = max(num1,num2);//optnext变为f(i+2)
}
return optnext;
}
}
四、总结
动态规划说到底是利用递推思想,对于动态规划首先要找到递推式和结束条件,以此为基础就可以一步步的去解决问题。
如果想再了解一些有关动态规划的问题,可以去搜索一下背包问题或者去leetcode上找一些相关的问题。