B - 鑰匙計數之一
一把鎖匙有N個槽,槽深爲1,2,3,4。每鎖匙至少有3個不同的深度且至少有1對相連的槽其深度之差爲3。求這樣的鎖匙的總數。
Input
本題無輸入
Output
對N>=2且N<=31,輸出滿足要求的鎖匙的總數。
Sample Output
N=2: 0 N=3: 8 N=4: 64 N=5: 360 .. .. .. .. .. .. .. N=31: ... 注:根據Pku Judge Online 1351 Number of Locks或 Xi'an 2002 改編,在那裏N<=16
【分析】
我做的時候沒有將遞歸/遞推的思想融進去,一直在想它排列組合的規律,而並沒有將其中每一項與其前一項聯繫起來,因爲我覺得這每一種情況增加一個“鑰匙槽”得到的下一種情況需要重新考慮,太離散,實際上遞推分情況考慮可以分析出來。
我們用a[i]來表示i個槽的滿足要求的鑰匙的總數。
則a[i]與a[i-1]之間有如下情況:
- 若前i-1個槽已經符合要求,則最後一個槽可以是1,2,3,4:a[i] = a[i-1]*4;
- 若前i-1個槽不符合要求,則分爲兩種情況:
- i-1個槽裏有相鄰且相差爲3的槽,則前i-1個槽裏一定只有兩種槽1和4,即1和4的全排列減去全爲4和全爲1的情況,那麼第i個槽一定爲3或2:a[i] = [2^(i-1)-2]*2;
- i-1個槽裏沒有相鄰且相差爲3的槽,要使i個槽符合要求,則前i-2個槽一定沒有(1,4)或(4,1),且第i-1個槽一定是1或4,最後一個槽是4或1,前i-2個槽是所有情況4^(i-2)減去只有1和4的情況2^(i-2),到此爲止我們只剩前i-1個槽符合要求的鑰匙數沒有被剔除:a[i] = [4^(i-2)-2^(i-2)]*2-b[i-1];
【代碼】
#include<iostream>
#include<math.h>
#define ll long long
using namespace std;
int main()
{
int i;
ll temp,a[32],b[32];//b[i]記錄以1 4結尾的數
a[2]=0;
a[3]=8;
b[2]=0;
b[3]=4;
printf("N=2: 0\nN=3: 8\n");
for(i=4;i<=31;i++)
{
a[i]=a[i-1]*4;//n-1已經合法
a[i]+=(ll)(2*(pow(2,i-1)-2));//n-1序列全爲1,4
temp=((ll)pow(4,i-2)-(ll)pow(2,i-2))*2-b[i-1];//結尾爲(1,4)且合法
a[i]+=temp;
b[i]=a[i-1]*2+temp;//n個序列,結尾爲1,4且合法,加上n-1個序列且合法的一半(1或者4)
printf("N=%d: %I64d\n",i,a[i]);
}
return 0;
}