HDU 1438 鑰匙計數之一 (狀態壓縮或遞推)
HDU 1438 鑰匙計數之一
Problem Description
一把鎖匙有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: ...
分析:
遞推規律,也找不到,下面有參考大佬的規律遞推
狀態壓縮解法也看了好久才看懂,狀態壓縮有點巧妙啊
#include<iostream> #include<cstring> using namespace std; const int N=40; long long dp[N][N][N][2]; ///第幾個槽,前面槽的狀態(包含幾個不同的深度),最後一個槽的深度,是否已經符合要求 int num[17]; int main() { memset(num,0,sizeof(num)); memset(dp,0,sizeof(dp)); for(int i=0;i<16;i++) for(int j=0;j<4;j++) if(i&(1<<j)) num[i]++;///找不同的狀態不同深度數。從而找出合法的至少有3個深度不同 for(int i=0;i<4;i++)///初始化1只有1,2,3,4四個狀態。 dp[1][1<<i][i][0]=1; for(int i=2;i<32;i++)///槽數 for(int j=0;j<16;j++)///不同深度狀態 for(int k=0;k<4;k++)///前一個槽的最後一個深度 { for(int l=0;l<4;l++)///當前槽的最後一個的深度 { int cur=j|(1<<l); ///在j的二進位制中第l位變為1(注意從後往前) dp[i][cur][l][1]+=dp[i-1][j][k][1]; if(k-l==3||k-l==-3) { dp[i][cur][l][1]+=dp[i-1][j][k][0]; } else dp[i][cur][l][0]+=dp[i-1][j][k][0]; } } for(int i=2;i<32;i++) { long long ans=0; for(int j=0;j<16;j++) if(num[j]>=3)///挑出符合條件的超過2個不同深度 for(int k=0;k<4;k++) ans+=dp[i][j][k][1]; cout<<"N="<<i<<": "<<ans<<"\n"; } }
又是一題遞推題,用遞推的思路去解。 說來慚愧,做這題的時候有個地方卡住了,直到看了別人的解題報告才豁然開朗(數學沒學好。。) 將lock[n]計做n個凹槽時,符合要求的鑰匙總數. 將one[n]計做n個凹槽時,第n個凹槽為1時符合要求的鑰匙總數. 將two[n]計做n個凹槽時,第n個凹槽為2時符合要求的鑰匙總數. 且1=4,2=3; 對與第n個凹槽,有兩種情況。 1.前n-1個凹槽以構成鑰匙,那麼對於第n個凹槽而言,無論第n個凹槽為1/2/3/4,都有lock[n-1]種情況。 2.前n-1個凹槽未構成鑰匙,加了第n個凹槽後才符合鑰匙規則,那麼分兩種情況 A.當第n個凹槽為 2/3時,前面n-1個凹槽必須為1/4構造而成的,出去兩種全為1/4的情況,即2^(n-1)-2種情況 B.當第n個凹槽為1/4時,前面n-1個凹槽的構成有多種情況,將這多種情況分析清楚: 假設第n個凹槽為1: 則前面n-1個凹槽的構成情況有如下幾種 { A1.前n-1個凹槽由4種類型組成,但未有相鄰兩個凹槽高度差為3的。 A2.前n-1個凹槽由4種類型組成,有相鄰兩個凹槽高度差為3的。 B1.前n-1個凹槽由3種類型組成,但未有相鄰兩個凹槽高度差為3的。 B2.前n-1個凹槽由3種類型組成,有相鄰兩個凹槽高度差為3的。 C1.前n-1個凹槽由2種類型組成,但未有相鄰兩個凹槽高度差為3的。 C2.前n-1個凹槽由2種類型組成,有相鄰兩個凹槽高度差為3的。 D. 前n-1個凹槽由1種類型組成。 } 由已知條件“前n-1個凹槽未構成鑰匙,加了第n個凹槽後才符合鑰匙規則” 可以將A2,B2,C2,D的全部情況排除,推得結論n-1個凹槽上必須為4,才符合已知條件。 即:A1+B1+C1=all-(A2+B2+C2+D)=1*4^(n-2)-(one[n-1]+1*2^(n-2)-1+1) 綜合以上兩種情況: one[n]=4^(n-2)-(one[n-1]+2^(n-2))+lock[n-1] two[n]=2^(n-1)-2+lock[n-1] lock[n]=2*(one[n]+two[n]) 參考:https://blog.csdn.net/yuanyunfeng3/article/details/40298903?utm_source=copy
#include<stdio.h>
#include<math.h>
int main()
{
__int64 one[32]={0},two[32]={0},lock[32]={0};
one[3]=2;
lock[2]=0;
lock[3]=8;
printf("N=2: 0\n");
printf("N=3: 8\n");
for(int n=4;n<32;n++)
{
one[n]=(__int64)pow((float)4,n-2)-(__int64)pow((float)2,n-2)-one[n-1]+lock[n-1];
two[n]=(__int64)pow((float)2,n-1)-2+lock[n-1];
lock[n]=2*(one[n]+two[n]);
printf("N=%d: %I64d\n",n,lock[n]);
}
getchar();
return 1;
}