题目大意:一个2*X的格子矩阵,请问有多少种刷油漆的方案?每次只能刷一个格子,刷完一个格子,然后只能刷它左右或者斜对角线上的格子。
题目分析:要求的是刷漆顺序的总数。
A | C | E | G | I | K | N | P |
B | D | F | H | J | M | O | Q |
假设我们有以上的格子矩阵,我们可以有以下几种刷漆方案:
一. 1.先刷第一列,然后刷第二列,再刷第三列…… 对应到表中,也就是先刷A,B两格,再刷C,D两格……如果用a[i]表示从一个端点(假设是A)开始的前i列的方法数,那么在这种情况下,a[i] = a[i-1]*2,因为每次要判断先刷上面那格,还是下面那格。所以a[1] = 1(默认从A开始刷), 当i>1时,a[i] = a[i-1]*2.
2.先刷A,然后刷C,然后回过头刷B,再刷D,然后就是从第三列的某个开始刷。 这种情况下就是:a[i] = a[i-2] * 4. 为什么是乘以4,可能是A->C->B->D->(E或F),也可能是A->D->B->C->(E或F). 难道不可以是ACDB嘛,这个真没有,如果有的话,刷完B,还怎么有间隔地刷E或F呢
3.先刷A,再刷C,再刷E,再刷G……也就是每列选取一个格子这样刷,这种情况:b[i] = b[i-1]*2,每列有两种情况,b[1]=1. 分析:可能在中途某个点返回去刷吗?不可能,因为一旦返回,无法刷另一边了。
综合1.2.3.三点,得到a[i] = (2*a[i-1]+4*a[i-2]+b[i]) * 4,因为可以从A,B,P,Q四个点出发,所以要乘以4.
二.分析完起始点是四个端点的情况,再来分析端点是中间列的某个点,假设是从第i列的上下某个点出发,前面的i-1列,方法数自然是2*b[i-1]+2*a[n-i],因为我选第i-1列开始刷时,也有两种选择,选上还是选下。同样的,后面的N-i列,方法数是2*b[n-i]*2*a[i-1],因为第i列也有上下两种选择,所以此种情况方法数:2*(2*b[i-1]*2*a[N-i]+2*a[i-1]*2*b[N-i])
综合一二两种情况,即可。注意,中间结果要取模,而且数值类型要取long long,不然在取模之前就已经超出范围了,怎么办。
代码展示:
#include <iostream>
using namespace std;
#define max 1000000007
int main(){
int N;
cin>>N;
long long a[1001],b[1001];
long long sum = 0;
b[1] = 1;
for(int i=2;i<=N;i++){
b[i] = (b[i-1] * 2) % max;
}
a[1] = 1;
a[2] = 6;
for(int i=3;i<=N;i++){
a[i] = (2*a[i-1]%max+4*a[i-2]%max+b[i]%max)%max;
}
sum = 4*a[N]%max;
for(int i=2;i<N;i++){
sum = (sum + 8*a[N-i]*b[i-1]%max+8*a[i-1]*b[N-i]%max)%max;
}
cout<<sum<<endl;
return 0;
}