zoukankan      html  css  js  c++  java
  • 从动态规划到卡特兰数——以一道上班问题为例

    题目
    一位大城市的律师在他住所以北n个街区和以东n个街区处工作,每天她走2n个街区去上班。如果他从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

    Input
    输入一共要穿过的街区数2n
    Output
    输出所有按要求可以到达上班点的道路数。
    Sample Input
    8
    Sample Output
    14

    题目理解
    这道题的意思是给你一个n*n的格子,如下图所示(图示是一个n==4的例子),

    一开始你在图中的左下角,你每次可以往右或者往上走一个格子的距离,那么当你走了2n次之后你将会到达右上角。
    为了方便起见,我们各图中的每一个点设置一个坐标(x,y),其中x代表该点水平方向上距离距离起点(即左下角的点)的格子数,y代表该点垂直方向上距离距离起点的格子数。建立如下规定以后,我们可以发现,左下角的点的坐标即为(0,0),右上角的点的坐标为(n,n)。
    我们可以发现,虚线及其上方的所有点的坐标(x,y)均满足条件x<=y,虚线及其下方的所有点的坐标(x,y)均满足条件x>=y。并且我们如果第一步踏出去进入了右边的(1,0)点,我们的足迹所能到的的点将是虚线及其下方的点;如果我们第一步踏出去进入了上边的(0,1)点,我们的足迹所能到的的点将是虚线及其上方的点。显而易见的是,虚线上方和虚线下方的区域是对称的,所以我们接下来只考虑虚线下方的区域。
    我们设f(x,y)为从(0,0)点走到(x,y)点的所有方案的数量,则:

    • 当y==0时,f(x,y)=1。
      因为当x>0时,(x,y)只能从他左边的点走过来;当x=0时,即原点,方案数也为1(对应图中的红点)
    • 当x>0且x==y时,f(x,y)=f(x,x)=f(x, x-1)
      (x,y)只能从(x-1,y)和(x,y-1)过来,但是我们目前考虑的是虚线及其下方的情况,所以此时(x,x)只能从(x, x-1)过来(对应图中的蓝点)
    • 当x>0且x>y时,f(x,y)=f(x,y-1)+f(x-1,y)

    而上面所述的就是动态规划中的状态转移方程。我们可以建一个二维数组f,f[x][y]表示f(x,y)的值,则程序如下:

    #include <iostream>
    using namespace std;
    int f[100][100], n;
    int main()
    {
        cin >> n;
        n /= 2;
        for (int i = 0; i <= n; i ++)
        {
            f[i][0] = 1;
        }
        for (int i = 1; i <= n; i ++)
        {
            for (int j = 1;j <= n; j ++)
            {
                if (i == j)
                {
                    f[i][j] = f[i][j-1];
                }
                else
                {
                    f[i][j] = f[i][j-1] + f[i-1][j];
                }
            }
        }
        cout << f[n][n] << endl;
        return 0;
    }
    

    卡特兰数

    我们发现在这道题目中我们要求的答案是f(n, n),其中答案的两个参数是相同的。那么既然两个参数是相同的,我们可否用一个函数g(x)来表示f(x,x)呢?
    答案是肯定的。
    在这道题目中,通过推导我们可以发现:
    g(x)=g(0)g(x-1)+g(1)g(x-2)+...+g(x-1)*g(0)
    当然卡特兰数还有很多的变式。我们现在就先跳过证明,利用上述公式来编写程序:

    #include <iostream>
    using namespace std;
    int n, f[100];
    int main()
    {
        cin >> n;
        n /= 2;
        f[0] = 1;
        for (int i = 1; i <= n; i ++)
        {
            for (int j = 0; j < i; j ++)
            {
                f[i] += f[j] * f[i-j-1];
            }
        }
        cout << f[n] << endl;
        return 0;
    }
    

    该程序同样可以求得正确的解法。

  • 相关阅读:
    python 注释
    python元祖
    浅谈单片机应用程序架构----本质是定时调用
    原子哥的STM32视频,我发现他们都看不懂原子哥里面按键扫描程序
    指针函数与函数指针的区别
    基于不带字库的图形LCD模块汉字显示解决方案
    GB2312编码
    C语言可变参简介
    kEIL5环境下移置STM32库文件
    nodejs表单验证
  • 原文地址:https://www.cnblogs.com/xianyue/p/6838037.html
Copyright © 2011-2022 走看看