格子刷油漆
问题描述
X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆。
你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!)
比如:a d b c e f 就是合格的刷漆顺序。
c e f d a b 是另一种合适的方案。
当已知 N 时,求总的方案数。当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模。
你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!)
比如:a d b c e f 就是合格的刷漆顺序。
c e f d a b 是另一种合适的方案。
当已知 N 时,求总的方案数。当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模。
输入格式
输入数据为一个正整数(不大于1000)
输出格式
输出数据为一个正整数。
样例输入
2
样例输出
24
样例输入
3
样例输出
96
样例输入
22
样例输出
359635897
Algorithm
fb(n) 从边缘某格开始,到与它相邻的另一个边缘格子结束
fb(n) = fb(n-1) * 2
fa(n) 从某个边缘格子开始的所有情况
fa(n) = fb(n) + 2*fa(n-1) + 4 * fa(n-2)
最后走相邻边缘格 第1步走相邻边缘格 第2步走相邻边缘格
fk(i,n) 一共有n个格子,从中间的第i个格子为起点,任意结束
fk(i,n) = ( fb(i)*fa(n-i)*2 + fb(n-i+1)*fa(i-1)*2 ) * 2
先走左边再右边 先走右边再左边 有两个可能起点
总情况包含:
从某个边缘格开始的所有情况 4 * fa(i)
从中间某个格子开始的所有情况 i从2到n-1求和:fk(i,n)
AC
1 #include<iostream> 2 #include<cstring> 3 4 using namespace std; 5 6 typedef long long ll; 7 8 const ll mod = 1e9 + 7; 9 10 ll fb[1007], fa[1007]; 11 12 ll fk(int i, int n) 13 { 14 return (fb[i]*fa[n-i]*2%mod + fb[n-i+1]*fa[i-1]*2%mod)*2%mod; 15 } 16 17 ll f(int n) 18 { 19 if(n == 1) return 2; 20 ll ans = fa[n]*4%mod; 21 for(int i=2;i<n;i++) 22 ans = (ans + fk(i, n))%mod; 23 return ans; 24 } 25 26 int main() 27 { 28 ll n = 0; 29 cin>>n; 30 memset(fa, 0, sizeof(fa)); 31 memset(fb, 0, sizeof(fb)); 32 fa[1] = 1; 33 fa[2] = 6; 34 fb[1] = 1; 35 // 预处理 fb 36 for(int i=2;i<1001;i++) 37 fb[i] = 2*fb[i-1]%mod; 38 for(int i=3;i<1001;i++) 39 fa[i] = (fb[i] + 2*fa[i-1] + 4*fa[i-2])%mod; 40 /* 41 for(int i=1;i<n;i++){ 42 cout<<i<<":"<<f(i)<<'\n'; 43 } 44 */ 45 cout<<f(n)<<'\n'; 46 return 0; 47 }
2019-03-17
20:40:10