战场的数目
在上题中,假设战场的图形周长为p,一共有多少种可能的战场?
例如,p<8时没有符合要求的战场,p=8时有2种战场:
p=10有9种战场:
要求输出方案总数模987654321的值。
7
8
9
10
0
0
2
0
9
无
想法:
看了别人代码,思考许久。
战场的定义比较趋近于俄罗斯方块,及不能有悬空的块;每个块下面必须为底或者是另一个块,并且这些块不能构成一个矩形。
直接入手比较麻烦。
很显然的就是高就是最高的块所在的层数,宽也是最后一层的宽度。
分情况讨论一下,显然只有 为偶数的时候才可能有答案,奇数一定无解。
1.如果左/右只有一边存在一个单独的块。
这样删掉这个块将会得到周长为p−2时的一种方案。
2.如果左/右都不存在单独一个块。
这样无法通过删单个块得到一种周长为p−2的方案,但是,可以通过去掉最后一层得到一种周长为p−2的方案。
3.如果左/右都存在一个单独的块。
这种方案,会在情况1中额外统计,所以要减去,而这样的方案数则对应是周长为p−4时的方案。
然后就可以得到递推式f(p) = 3*f(p - 2) - f(p - 4) (p>4)f[p]=2∗f[p−2]+f[p−2]−f[p−4]=3∗f[p−2]−f[p−4
然后这个显然是可以矩阵乘法优化的,不过这样会产生一些不必要的内存,所以我们把p=p/2然后就可以得到
f[p]=3∗f[p−1]−f[p−2]
这样得到的答案,是满足的情况,是包含正好组成矩形的情况的,所以我们在最后把它减去即可。
又易知P=4时,有1种,P=6时,有2种,P=8时,有5种
发现f【p】为第(p-4+1)个斐波拉契数
代码:
#include <stdio.h> #include <algorithm> using namespace std; typedef long long ll; const ll mod=987654321; ll A[2][2],B[2][2],T[2][2]; void pow(int n)//求第n+1个的斐波拉契数 { if(n==0) { //for(int i=0;i<2;i++) //for(int j=0;j<2;j++) // B[i][j]=(i==j); B[0][0]=1;B[0][1]=0;B[1][0]=0;B[1][1]=1; return; } if(n&1) { pow(n-1); for(int i=0;i<2;i++) for(int j=0;j<2;j++) { T[i][j]=0; for(int k=0;k<2;k++) T[i][j]=(T[i][j]+A[i][k]*B[k][j])%mod; } for(int i=0;i<2;i++) for(int j=0;j<2;j++) { B[i][j]=T[i][j]; } } else { pow(n/2); for(int i=0;i<2;i++) for(int j=0;j<2;j++) { T[i][j]=0; for(int k=0;k<2;k++) T[i][j]=(T[i][j]+B[i][k]*B[k][j])%mod; } for(int i=0;i<2;i++) for(int j=0;j<2;j++) { B[i][j]=T[i][j]; } } } int main() { int n; A[0][0]=1; A[0][1]=1; A[1][0]=1; A[1][1]=0; while (scanf("%d", &n)== 1 && n) { ll ans=0; if(n&1) ans=0; else if(n<4) ans=0; else { pow(n-4);//求出B【0】【0】为周长为n的个数 ans=B[0][0]-(n/2-1);//减去矩形个数 ans%=mod; if(ans<0) ans+=mod; } //printf("%lld\n",B[0][0]); printf("%lld\n",ans); } return 0; }

本文介绍了一种通过矩阵乘法优化的递推算法来解决特定战场图形计数问题的方法。该算法利用递推式f(p)=3*f(p-2)-f(p-4)进行计算,并考虑了边界条件和特殊情况。
1692

被折叠的 条评论
为什么被折叠?



