湖南省第六届大学生计算机程序设计竞赛 战场的数目

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

战场的数目


 

在上题中,假设战场的图形周长为p,一共有多少种可能的战场?

例如,p<8时没有符合要求的战场,p=8时有2种战场:


p=10有9种战场:


要求输出方案总数模987654321的值。


Input
输入文件最多包含25组测试数据,每个数据仅包含一行,有一个整数p(1<=p<=109),表示战场的图形周长。p=0表示输入结束,你的程序不应当处理这一行。
Output
对于每组数据,输出仅一行,即满足条件的战场总数除以987654321的余数。
Sample Input
7
8
9
10
0
Sample Output
0
2
0
9
Hint
想法:
看了别人代码,思考许久。

战场的定义比较趋近于俄罗斯方块,及不能有悬空的块;每个块下面必须为底或者是另一个块,并且这些块不能构成一个矩形。

直接入手比较麻烦。

很显然的就是高就是最高的块所在的层数,宽也是最后一层的宽度。

分情况讨论一下,显然只有 p为偶数的时候才可能有答案,奇数一定无解。

1.如果左/右只有一边存在一个单独的块。

这样删掉这个块将会得到周长为p2时的一种方案。

2.如果左/右都不存在单独一个块。

这样无法通过删单个块得到一种周长为p2的方案,但是,可以通过去掉最后一层得到一种周长为p2的方案。

3.如果左/右都存在一个单独的块。

这种方案,会在情况1中额外统计,所以要减去,而这样的方案数则对应是周长为p4时的方案。

然后就可以得到递推式f(p) = 3*f(p - 2) - f(p - 4) (p>4)f[p]=2f[p2]+f[p2]f[p4]=3f[p2]f[p4

然后这个显然是可以矩阵乘法优化的,不过这样会产生一些不必要的内存,所以我们把p=p/2然后就可以得到

f[p]=3f[p1]f[p2]

这样得到的答案,是满足的情况,是包含正好组成矩形的情况的,所以我们在最后把它减去即可。

又易知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; }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值