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

题目详情

我们有一个两行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;
}
时间: 2024-11-08 21:12:03

【题解】 第五届在线编程大赛月赛第二题:走格子的相关文章

第五届在线编程大赛月赛第一题:完全平方数的个数

第五届在线编程大赛月赛第一题:完全平方数的个数 题目详情: 给定整数区间[A,B]问其中有多少个完全平方数. 输入格式: 多组数据,包含两个正整数A,B 1<=A<=B<=2000000000. 输出格式: 每组数据输出一行包含一个整数,表示闭区间[A,B]中包含的完全平方数的个数. 答题说明: 输入样例 1 1 1 2 3 10 3 3 输出样例: 1 1 2 0 java代码: import java.util.Scanner; public class One { public s

英雄会题目解析- 第五届在线编程大赛月赛第三题:石子游戏

题目: 甲乙两人面对若干堆石子,其中每一堆石子的数目可以任意确定.两人轮流按下列规则取走一些石子,游戏的规则如下:1.每一步应取走至少一枚石子:2.每一步只能从某一堆中取走部分或全部石子:3.如果谁无法按规则取子,谁就是输家.如果甲乙两人都采取最优的策略,甲先拿,请问,是甲必胜还是乙必胜.输入格式:多组数据,每组数据两行,第一行是一个整数N, 2<=N<=10000下一行是N个正整数,代表每堆的石子数,石子数在32位整数内.输出格式:每组测试数据输出一行,如果甲存在必胜策略,输出"W

CSDN英雄会-第五届在线编程大赛月赛第三题:石子游戏(1)

题目详情 甲乙两人面对若干堆石子,其中每一堆石子的数目可以任意确定. 两人轮流按下列规则取走一些石子,游戏的规则如下: 1.每一步应取走至少一枚石子: 2.每一步只能从某一堆中取走部分或全部石子: 3.如果谁无法按规则取子,谁就是输家. 如果甲乙两人都采取最优的策略,甲先拿,请问,是甲必胜还是乙必胜. 输入格式: 多组数据,每组数据两行,第一行是一个整数N, 2<=N<=10000 下一行是N个正整数,代表每堆的石子数,石子数在32位整数内. 输出格式: 每组测试数据输出一行,如果甲存在必胜策

csdn第五届在线编程大赛-完全平方

题目详情 给定整数区间[A,B]问其中有多少个完全平方数. 输入格式: 多组数据,包含两个正整数A,B 1<=A<=B<=2000000000. 输出格式: 每组数据输出一行包含一个整数,表示闭区间[A,B]中包含的完全平方数的个数. 答题说明 输入样例 1 1 1 2 3 10 3 3 输出样例: 1 1 2 0 解答: #include<iostream> #include<cmath> using namespace std; int main() { do

csdn第五届在线编程大赛-全然平方

题目详情 给定整数区间[A,B]问当中有多少个全然平方数. 输入格式: 多组数据,包括两个正整数A,B 1<=A<=B<=2000000000. 输出格式: 每组数据输出一行包括一个整数.表示闭区间[A,B]中包括的全然平方数的个数. 答题说明 输入例子 1 1 1 2 3 10 3 3 输出例子: 1 1 2 0 解答: #include<iostream> #include<cmath> using namespace std; int main() { do

【平安科技】在线编程大赛活动

1. 活动名称:[现金大奖]平安科技在线编程大赛:一份"奇妙"的银行流水 题目详情: 一份银行流水数据,因打印模糊导致部分金额不清楚. 收入.支出.余额满足以下3条规则: 1.收入.支出.余额三列都是数字 2.同一行收入和支出的值不能同时为非零值 3.第N-1行余额(+第N行收入或-第N行支出)=第N行余额 程序语言: java 请按照规则编写算法,修复不清楚的值 输入描述: 输入数据最多25行,每行都包含四个数据,分别是:数据编号,收入.支出.余额,模糊的数据以?表示,它们之间以;隔

第二届战神杯线上编程挑战赛月赛第一题:回文数

题目详情: Njzy学习了回文串后联想到了回文数,他希望统计出一个区间内的全部回文数.如今给定一个闭区间[a,b],求这个区间里有多少个回文数. 比方[20,30],仅仅有一个回文数那就是22. 输入描写叙述: 输入包括多组測试数据,每组測试数据包括两个整数a,b, (0<a<=b<10^6). 输出描写叙述: 对于每组測试数据输出对应的答案. 答题说明: 输入例子: 1 10 20 30 300 400 输出例子: 9 1 10 解题思路: total[i]代表从1到i之间有多少回文数

编程之美第二题 找连续数

题意:   就是给你一个乱序序列,  现在要找某个特定序列的个数,   某特定序列的要求为  该序列长度为k   且序列中为连续的序列   例如 13245  就为1-5的连续序列 题解: 没啥解法   ,  两重for 暴力,就是这样 代码: #include<stdio.h> #include<iostream> #include<algorithm> #include<map> #include<string.h> using namesp

CSDN第四届在线编程大赛2014初赛:带通配符的数

题目要求: 输入参数:参数A,含有任意个数的?的数值字符串,如:12?4,?代表一位任意数             参数B,不含?的数值字符串,长度与参数A一致输出结果:参数A比参数B大的可能数值个数 输入样例36?1?82364288?3910?5输出样例10004 [cpp] view plaincopy #define num_max_length 20 int main(void) { char num1[num_max_length]; char num2[num_max_length