题意
有一块n*2的巧克力,将它分成k块,问有多少种方法。
分析
emmm是dp没错了。
最容易想到的状态定义是f[i][j],意思是前i行,分成j块的方案数。但是发现没法转移。(后面会说一下为什么···)
我们把状态定义为f[i][j][0]和f[i][j][1]。
f[i][j][0]:前i行分成j块,且第i行的两小块巧克力是没有连在一起的。
f[i][j][1]:前i行分成j块,且第i行的两小块巧克力是连在一起的。
我们来把转移分一下类。
情况1:从i行到i+1行的时候,巧克力的块数多了两块。这说明,第i+1行的两小块一定是分开的,而且没有和第i行的相连。那么转移只有一种情况f[i][j][0]=f[i-1][j-2][0]+f[i-1][j-2][1]
情况2:从i行到i+1行的时候,巧克力的块数多了一块。如果第i+1行的两小块是连在一起的一整块,那么一定没有和i行的相连。既f[i][j][1]=f[i-1][j-1][0]+f[i-1][j-1][1]。如果第i+1行的两小块是分开的,那么一定有一块是和i行相连。既f[i][j][0]=f[i-1][j-1][1]*2+f[i-1][j-1][0]*2
情况3:从i行到i+1行的时候,巧克力的块数没有增加。这就说明第i+1行的一定是和i行相连的。如果第i+1行两小块是分开的,那么第i行一定是分开的。所以f[i][j][0]=f[i-1][j][0]。如果i+1行两小块是和在一起的,那么就要分类讨论。
思路大概就是这个样子。。。
至于为什么简单的定义为f[i][j]没法转移,因为,我试过了···他就是没法转移···········
咳咳不闹,我们来看第三种情况,他的转移是和前一行是分开还是连在一起的有关。所以我们要表示出这个状态。
下面是代码,我尽量写的可读性强一些了···
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 using namespace std; 7 const int maxn=1000+100; 8 const int MOD=100000007; 9 int n,k,T; 10 int f[maxn][2*maxn][3];//0分开,1和起来 11 int main(){ 12 scanf("%d",&T); 13 for(int t=1;t<=T;t++){ 14 memset(f,0,sizeof(f)); 15 scanf("%d%d",&n,&k); 16 f[1][1][1]=f[1][2][0]=1; 17 for(int i=2;i<=n;i++){ 18 f[i][2*i][0]=1;f[i][1][1]=1; 19 for(int j=2;j<2*i;j++){ 20 //******第1,2种情况*********** 21 f[i][j][1]=(f[i-1][j-1][1]+f[i-1][j-1][0])%MOD;//1.1 22 f[i][j][0]=(f[i-1][j-1][1]*2+f[i-1][j-1][0]*2)%MOD;//2.1 23 f[i][j][0]=(f[i][j][0]+f[i-1][j-2][1]+f[i-1][j-2][0])%MOD;//2.2 24 25 //*********第3种情况************** 26 f[i][j][1]=(f[i][j][1]+f[i-1][j][0]*2+f[i-1][j][1])%MOD; 27 f[i][j][0]=(f[i][j][0]+f[i-1][j][0])%MOD; 28 } 29 } 30 int ans=(f[n][k][0]+f[n][k][1])%MOD; 31 printf("%d ",ans); 32 } 33 return 0; 34 }