湖南省第六屆大學生程式競賽--戰場的數目
題目I
戰場的數目
在上題中,假設戰場的圖形周長為p,一共有多少種可能的戰場?
例如,p<8時沒有符合要求的戰場,p=8時有2種戰場:
p=10有9種戰場:
要求輸出方案總數模987654321的值。
輸入
輸入檔案最多包含25組測試資料,每個資料僅包含一行,有一個整數p(1<=p<=109),表示戰場的圖形周長。p=0表示輸入結束,你的程式不應當處理這一行。
輸出
對於每組資料,輸出僅一行,即滿足條件的戰場總數除以987654321的餘數。
樣例輸入 |
樣例輸出 |
7 8 9 10 0 |
0 2 0 9 |
==============================================
這道題開始想著構建一個二維陣列,然後列舉。。不過2s後急忙否定了這個答案,因為周長《=1000000000
後來想那就遞迴,先找出一種情況,例如p=8時候戰場的形狀,再來遞迴下一種情況,也就是p=10的情況(加上一些方塊,使得周長+2,就看加上方塊的種類個數 也就變成了 f(10) = f(8) + h(8) h(8)代表可以加上幾種方塊。) 後來水平有限,也覺得稍稍複雜,就沒繼續想此方法,希望有高手能指教。
後來經群裡一大神提醒,立馬明白了這道題。
在說這道題之前,要說一下這道題目的限制條件。在上一題中,題目限制戰場不能為矩形,並且要考慮重力。也就是說不能堆出一些奇奇怪怪的戰場。
好,開始分析題目吧
==============================================
此題其實暗含規律,找到規律即可輕鬆解決。
規律如下:
先考慮矩形情況,也就是P=4時,有1種,P=6時,有2種
1.若戰場最左邊有高度為1的方塊,那麼若將此方塊減掉,則P-2。
2.若戰場最右邊有高度為1的方塊,那麼若將此方塊減掉,則P-2。
3.若戰場的最下層去掉,那麼P-2; (只減少左右兩邊的周長)
4.若戰場的左邊和右邊皆有高度為1的方塊,由於1.2情況包含此情況,因此要減去此情況。此時P-4.
那麼,得到的規律即為:f(p) = 3*f(p - 2) - f(p - 4) (p>4)
基於此規律 即可得到遞推公式 思路也就明確了。
在此使用矩陣快速冪,若不清楚,請參考:http://www.cnblogs.com/dongsheng/archive/2013/06/02/3114073.html
在此附上程式碼:
#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)
{
if(n==0)
{
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
B[i][j]=(i==j);
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);
ans=B[0][0]-n/2+1;
ans%=mod;
if(ans<0)ans+=mod;
}
printf("%lld\n",ans);
}
return 0;
}