这篇博客主要讲的是动态规划入门,即动态规划的思想,并且再讲解动态规划的最简单的一个方法。
首先,什么是动态规划?
动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。其实就是分解问题,分而治之。可能这样说大家都不太理解,其实这个有点类似于数学中的递推公式。来举一个简单的例子,看下边这个题:
N阶楼梯上楼问题:一次可以走两阶或一阶,问有多少种上楼方式。
这就是动态规划就简单的一个列子。拿到这个题,大家是不是都有点迷,这个到底怎么做?是不是没有思路,那么按照动态规划思想,我们可以先分析下问题,每次都有两种跳法,分别是一阶或者两阶,那么如果当前是第n个台阶,那么跳法是不是是(n-1)台阶的跳法数加上(n-2)台阶的跳法数?如果划算成公式是F(n) = F(n-1)+F(n-2)。F(n)代表第n阶台阶的跳法的数量。
这不就相当于找到了一个递推公式,然后来进行计算。具体的代码实现如下(采用的是非递归):
#include <stdio.h> int main() { int i,N; long long a[90]; while(~scanf("%d",&N)) { a[1]=1; a[2]=2; for(i=3;i<=N;i++) a[i]=a[i-1]+a[i-2]; printf("%lld\n",a[N]); } return 0; }
下边再举一个列子来辅助理解:
n封信放入n个信封,要求全部放错,共有多少种放法,记n个元素的错排总数为f(n)
对于这个题,我们可以采用和上述一样的思路,我们是否要考虑下找一个类似的递推公式等来解决。思路如下:
在任意一种错装方案中,假设n号信封里装的是k号信封的信,而n号信封里的信则装在m号信封里。我们按照k和m的等值与否将总的错装方式分为两类。 若k不等于m,交换n号信封和m号信封的信后,n号信封里装的恰好是对应的信,而m号信封中错装k号信封里的信,即除n号信封外其余n-1个信封 全部错装,其错装方式等于 F[n - 1],又由于m的n-1个可能取值,这类错装方式总数为(n - 1)* F[n - 1]。也可以理解为,在 n-1个信封错装的 F[n - 1]种方式的基础上,将n号信封所装的信与n - 1个信封中任意一个信封(共有 n-1 中选 择)所装的信做交换后,得到所有信封全部错装的方式数。另一种情况,若 k 等于 m,交换 n 号信封和 m 号信封的信后,n 号信封和 m 号信封里装的恰好是对应的信,这样除它们之外剩余的 n-2 个信封全部错装,其 错装方式为 F[n - 2],又由于 m 的 n-1 个取值,这类错装方式总数为(n - 1)* F[n - 2]。也可以理解为,在 n - 2 个信封全部错装的基础上,交换最后两个信封中的 信(n 号信封和 1 到 n-1 号信封中任意一个,共有 n-1 种选择),使所有的信封全部 错装的方式数。 综上所述,F[n] = (n - 1) * F[n - 1] + (n - 1) * F[n - 2]。这就是错排公式。
具体代码如下:
#include <stdio.h> Long long F[21]; //数值较大选用long long int main () { F[1] = 0; F[2] = 1; //初始值 for (int i = 3;i <= 20;i ++) F[i] = (i - 1) * F[i - 1] + (i - 1) * F[i - 2]; //递推求得数列每一个数字 int n; while (scanf ("%d",&n) != EOF) { printf("%lld\n",F[n]); //输出 } return 0; }