弦月的博客
锦瑟无端五十弦,一弦一柱思华年。

题目
一位大城市的律师在他住所以北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;
}

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

posted on 2017-05-10 20:48  弦月C  阅读(606)  评论(0编辑  收藏  举报