描述
前两周里,我们讲解了2xN,3xN骨牌覆盖的问题,并且引入了两种不同的递推方法。
这一次我们再加强一次题目,对于给定的K和N,我们需要去求KxN棋盘的覆盖方案数。
输入
第1行:2个整数N。表示棋盘宽度为k,长度为N。2≤K≤7,1≤N≤100,000,000
输出
第1行:1个整数,表示覆盖方案数 MOD 12357
2 62247088
1399
在2xN的骨牌问题中,我们有答案的递推序列。f[n] = f[n-1]+f[n-2]。
事实上在处理3xN的问题中,也有部分选手推导出了答案的递推序列。
那么对于4xN,5xN,是否也存在答案的递推序列呢?有兴趣的选手不妨尝试推导一下。
在上一期,也就是3xN问题中,我们介绍了根据状态来递推的方法。这种方法显然是通用性最好的,可以用来解决任何K值的覆盖。
对于任意一个K值,我们每一行拥有的状态数量为2^K种。
在K=3时,我们是通过手动来枚举的8种状态之间的递推关系。
当K=4或者更大的时候,再通过手动枚举就显得不那么科学了,此时我们需要考虑如何用程序生成我们所需要的状态转移矩阵。
让我们再回头看看我们上一期提示里面放置骨牌的约定:
假设我们正在放置第i行的骨牌,那么会有下面3种方式:
灰色表示已经有的骨牌,绿色表示新放置的骨牌。
每一种放置方法解释如下,假设当第i行的状态为x,第i-1行的状态为y:
- 第i行不放置,则前一行必须有放置的骨牌。x对应二进制位为0,y对应二进制位为1。
- 第i行竖放骨牌,则前一行必须为空。x对应二进制位为1,y对应二进制位为0。
- 第i行横向骨牌,则前一行必须两个位置均有骨牌,否则会产生空位。x对应二进制位为1,y对应二进制位为1。
既然有对应的二进制描述,那么上面三种方法就可以用程序语言解释为: - 第i行不放置:new_x = x << 1, new_y = (y << 1) + 1; 列数+1
- 第i行竖放骨牌:new_x = (x << 1) + 1, new_y = y << 1; 列数+1
- 第i行横向骨牌:new x = (x << 2) + 3, new_y = (y << 2) + 3; 列数+2
通过迭代去枚举3种放置方法,当总的列数等于K时,此时的x便可由y转移过来。那么我们可以得到枚举放置的伪代码:
DFS(x, y, col):
If col == K
d[y][x] = 1
Return ;
End
DFS(x << 1, (y << 1) + 1, col + 1);
DFS((x << 1) + 1, y << 1, col + 1);
If col + 2 <= K
DFS( (x << 2) + 3, (y << 2) + 3, col + 2 )
End
当我们得到对应的矩阵之后,剩下需要做的也就和上一期相同了。
在某些题目中有可能会出现,N很小,K很大的情况。比如N=20,K=14这样的情况。
考虑到N很小,我们可以不使用矩阵乘法,而直接采用f[i-1]到f[i]行的递推。时间复杂度也就转化为2^(2k)*N。
但是状态数量为2^14,也就是16384种。若采用转移矩阵,肯定是无法储存的。而实际情况是在转移矩阵中1的数量并不多,所以我们可以考虑存储为(y,x)这样的二元组。在转移过程中只枚举合法的转移即可。
若K再更大一点,比如K=20,产生的状态有可能连开数组存储都很吃力。这个时候我们也可以考虑在计算每一行时,直接通过dfs来进行转移,不储存转移关系。用时间来换取空间。
#include <iostream> #include<bits/stdc++.h> #define ll long long using namespace std; typedef vector<ll> vec; typedef vector<vec> mat; const ll MOD=12357; mat mul(mat &A,mat &B) { mat C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++) for(int k=0;k<B.size();k++) for(int j=0;j<B[0].size();j++) C[i][j]=(C[i][j]+A[i][k]*B[k][j])%MOD; return C; } mat pow(mat A, ll n) { mat B(A.size(),vec(A.size())); for(int i=0;i<A.size();i++) B[i][i]=1; while(n>0) { if(n&1) B=mul(B,A); A=mul(A,A); n>>=1; } return B; } int k; ll d[220][220]; void dfs(int x,int y,int col) { if(col==k) { d[y][x]=1; return; } dfs(x<<1,(y<<1)+1,col+1); dfs((x<<1)+1,y<<1,col+1); if(col+2<=k) dfs((x<<2)+3,(y<<2)+3,col+2); } int main() { ll n; while(~scanf("%d%lld",&k,&n)) { dfs(0,0,0); ll t=1<<k; mat A(t,vec(t)); for(int i=0;i<t;i++) for(int j=0;j<t;j++) A[i][j]=d[i][j]; A=pow(A,n); mat B(1,vec(t)); B[0][t-1]=1; B=mul(B,A); printf("%lld\n",B[0][t-1]); } } //2 62247088