【题解】 英雄会-第五届在线编程大赛月赛第二题:走格子

题目详情

我们有一个两行n列格子的棋盘,你可以从任何位置出发。每次你可以沿着上下左右以及对角线的方向走一格(不能出去),求有多少条可能的哈密尔顿路?(即所有的格子只经过一次的路。)

例如:

a b c

d e f

一条可能的路径是b,f,c,e,d,a

输入格式:

多组数据,每组数据1行,包含一个正整数n表示列数。 (n <= 1000)

输出格式:

每组数据输出一行包含一个整数,可能的路径条数。结果比较大,输出对10^9 + 7的结果

答题说明

输入样例

1

2

3

输出样例:

2

24

96

这道题需要对题目抽象一下,然后抽象出的模型进行递推总结规律,最后还用到同余定理。

首先题目中给出了2 x n的方格,要求是求出有多少个哈密顿路。有意思的是,可以沿着上下左右以及对角线的方向走一格。
模拟一下,假设有个小人从某一个起点出发,在不看纵方向,只看横方向,那么对于2 x n这样的方格,放置位置就会有n种选择,假设放在从左往右起第i个格子组上,那么就会有2种放置方式(在上边或放在下面)那么向左或者向右移动的话,也会有两种移动方式(譬如向右吧,就会有移动到右上或移动到右下两种移动方式)
换句话说,对于 2 x n这样的方格,遍历所有格子的移动可能性为2的n次方。
为了方便问题的解决,我们需要屏蔽掉移动可能性数,于是做这样的抽象:
将2 x n的方格抽象为n维向量,定义向量中的第i维内的数的意义为:已经路过了从左向右第i组格子几次。
那么初始情况下这个向量一定是个0向量,且向量中的元素一定是小于等于2的非负整数。
考虑样例:
n=1时,有2种选择,这个样例可以忽略
n=2时,有24种选择,我们将24除以2的二次方得到6,6即为在我们抽象的出的模型内的路径数。
n=3时,有96种选择,我们将96除以2的三次方得到12,12即为在我们抽象出的模型内的路径数。

n=1时,是一个一维向量,所以不用过多考虑
n=2时,是一个二维向量,我们需要对它进行进一步讨论
如果将移动指针初始点放置在1位置,那么这三条路径为:
[1,0] -> [2,0] -> [2,1] -> [2,2]
[1,0] -> [1,1] -> [2,1] -> [2,2]
[1,0] -> [1,1] -> [1,2] -> [2,2]
由于是对称的,所以移动指针初始点放置在2位置,也为三条路径,不赘述。
仔细观察这三种路径,我们可以将其分为两类:一类终点在2位置,另一类重点在原位置。
我们推广一下,对于一个n维向量:
第一类:
[1,0,…0] -> [2,0,…,0] ->[2,1,0,…,0]->…
[1,0,…,0] -> [1,1,0,…,0] -> [2,1,0,…,0] -> [2,2,0,…,0] -> [2,2,1,0,…,0] -> …
第二类:
[1,0,…,0] -> [1,1,0,…,0] -> [1,1,1,0,…,0] -> [1,1,…,1] -> [1,1,…,1,2] -> [1,1,…,1,2,2] -> [2,…,2]
只能这么走,因为在一开始就暂停在原地,就是第一类的第一种情况,在中途暂停或向后走,则将不满足题目要求。

我们假设有这样一个函数f(n),其意义为一个n维向量,若起点在第1位置,则可能的路径数。
那么我们可以总结得到这样一个递推公式:
f(n) = f(n-1) + f(n-2) + 1
当然这是边界情况,那如果起点是非边界呢?
那么移动选择仅可能是向左或者向右而不能暂留。不论选择向左还是选择向右,必须要回到起点,考虑上述第一类移动方式和第二类移动方式,那么无论最初的向左移动还是向右移动,这一半的路径数为1(在抽象模型情况之上是这样哒)。那么剩下的格子的移动方式数就雷同于边界情况了,换句话说,初始移动点为非边界情况可以通过化为两个初始移动点位边界情况来得到。
于是对于起点为从左向右第i个格子(i != 1 且 i != n),可以得到这样的公式:
g(i) = f(i-1) + f(n - i)

为了验证真伪,通过n=3来进行验证。
n=3
f(1) = 1;
f(2) = 3;
f(3) = f(1) + f(2) + 1 = 5
存在两个边界和一个中间点,中间点计算结果为
g(2) = f(1) + f(1) = 2
那么最终结果为 2 + 5 * 2 = 12
再将12乘2的三次方:12 * 8 = 96
问题解决。

实现代码如下:

#include <iostream>
#define MOD(i) (i) % 1000000007
long long border[1010] = {0};
int bValid = 0;
long long modIndex[1010] = {0};
int mValid = 0;

void init(){
    border[0] = 0;
    border[1] = 1;
    border[2] = 3;
    border[3] = 5;
    bValid = 3;

    modIndex[0] = 1;
    modIndex[1] = 2;
    modIndex[2] = 4;
    modIndex[3] = 8;
    mValid = 3;
}

long long GetBorderModValue(int i){
    while(bValid < i) border[++bValid] = MOD(border[bValid-1]+border[bValid-2]+1);
    return border[i];
}

long long GetIndexModValue(int i){
    while(mValid < i) modIndex[++mValid] = MOD(modIndex[mValid-1]*2);
    return modIndex[i];
}

int main(){
    using namespace std;
    init();
    int n;
    while(cin >> n){
        long long GMidValue = 0;
        for(int i=1; i<=n;i++){
            if(i == 1 || i == n) GMidValue = MOD(GMidValue + GetBorderModValue(n));
            else GMidValue = MOD(GMidValue + MOD(GetBorderModValue(i-1) + GetBorderModValue(n-i)));
        }
        cout << MOD(GMidValue * GetIndexModValue(n)) << endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章