算法之动态规划(递推求解一)

这篇博客主要讲的是动态规划入门,即动态规划的思想,并且再讲解动态规划的最简单的一个方法。

首先,什么是动态规划?

  动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。其实就是分解问题,分而治之。可能这样说大家都不太理解,其实这个有点类似于数学中的递推公式。来举一个简单的例子,看下边这个题:

  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;
}

 

  

  

posted @ 2017-09-23 18:03  那年盛夏  阅读(3041)  评论(0编辑  收藏  举报