从动态规划到卡特兰数——以一道上班问题为例

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

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

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

一开始你在图中的左下角,你每次可以往右或者往上走一个格子的距离,那么当你走了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;
}

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

转载于:https://www.cnblogs.com/xianyue/p/6838037.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值