首页 > 代码库 > 骨牌覆盖小结

骨牌覆盖小结

题目链接

棋盘规模 2*n  (n很大)

直接找出递推公式用矩阵快速幂求解

技术分享
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2;
const LL mod=19999997;
LL b[N]= {1,1};        //此处初始化列向量 
LL hh[N][N]={{1,1},
             {1,0}
};

struct Mat
{
    LL mat[N][N];
    LL* operator [](int x)    //注意这种写法 
    {
        return mat[x];
    }
} A;
Mat Mut(Mat a,Mat b)
{
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=0; i<N; i++)
        for(int k=0; k<N; k++)    if(a[i][k])
            for(int j=0; j<N; j++)
            {
                c[i][j]+=a[i][k]*b[k][j]%mod;
                c[i][j]=c[i][j]%mod;
            }
    return c;
}
Mat Qpow(Mat a,LL n)
{
    Mat c;
    for(int i=0; i<N; ++i)
        for(int j=0; j<N; ++j)
            c[i][j]=(i==j);
    for(; n; n>>=1)
    {
        if(n&1) c=Mut(c,a);
        a=Mut(a,a);
    }
    return c;
}
LL cal(Mat A,LL n,LL b[])
{
    Mat A_=Qpow(A,n-1);
    LL ret=A_[0][0]*b[0]+A_[0][1]*b[1];
    return (ret+mod)%mod;
}
void init_A()
{
    for(int i=0; i<N; i++)
        for(int j=0; j<N; j++)
            A[i][j]=hh[i][j];
}

int main()
{
    LL n,ans;
    init_A();
    while(cin>>n)
    {
        ans=cal(A,n,b);        //假定下标从第1项开始计数,求第n项 
        cout<<ans<<endl;
    }
} 
Lv. 0

 

题目链接

棋盘规模 k*n  (k<=7,n很大)

利用dfs推出状态转移矩阵(矩阵规模 (2k))

有一点要注意的是,比如说求的是两列的递推矩阵,那么前一列如果对应位是0,那么递推时,要在该位置横放一个骨牌,使当前列该为1

然后用矩阵快速幂求解

技术分享
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=128;
const LL mod=12357;

int n,k,M;

LL b[N];//= {0,0,0,0,0,0,0,1};        //此处初始化列向量 

struct Mat
{
    LL mat[N][N];
    LL* operator [](int x)    //注意这种写法 
    {
        return mat[x];
    }
} A;
Mat Mut(Mat a,Mat b)
{
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=0; i<M; i++)
        for(int k=0; k<M; k++)    if(a[i][k])
            for(int j=0; j<M; j++)
            {
                c[i][j]+=a[i][k]*b[k][j]%mod;
                c[i][j]=c[i][j]%mod;
            }
    return c;
}
Mat Qpow(Mat a,LL n)
{
    Mat c;
    for(int i=0; i<M; ++i)
        for(int j=0; j<M; ++j)
            c[i][j]=(i==j);
    for(; n; n>>=1)
    {
        if(n&1) c=Mut(c,a);
        a=Mut(a,a);
    }
    return c;
}
LL cal(Mat A,LL n,LL b[])
{
    Mat A_=Qpow(A,n);
    return A_[M-1][M-1];
}
void dfs(int pre,int nxt,int col,Mat& A)
{
    if(col==k)
    {
        A[pre][nxt]=1;
        return ;
    }
    dfs(pre<<1,nxt<<1|1,col+1,A);
    dfs(pre<<1|1,nxt<<1,col+1,A);
    if(col+2<=k)    dfs(pre<<2|3,nxt<<2|3,col+2,A);
}



int main()
{
    while(cin>>k>>n)
    {
        memset(b,0,sizeof(b));
        M=1<<k;
        b[M-1]=1;
        memset(A.mat,0,sizeof(A.mat));
        dfs(0,0,0,A);
        LL ans=cal(A,n,b);        //假定下标从第1项开始计数,求第n项 
        cout<<ans<<endl;
    }
} 
Lv. 1

 

题目链接

棋盘规模 n*m  (n,m<=11)

这时矩阵乘法的复杂度就太大了,可以直接利用利用递推关系搞

技术分享
//#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL;
const int N=2048;

int n,k,M;
int A[13][N];

void dfs(int pre,int nxt,int col,int r)
{
    if(col==k)
    {
        A[r][nxt]+=A[r-1][pre];
        return ;
    }
    dfs(pre<<1,nxt<<1|1,col+1,r);
    dfs(pre<<1|1,nxt<<1,col+1,r);
    if(col+2<=k)    dfs(pre<<2|3,nxt<<2|3,col+2,r);
}

int main()
{
    while(cin>>k>>n)
    {
//        cout<<k<<‘ ‘<<n<<endl;
        if(k==0&&n==0)
            break;
        if(k%2&&n%2)
        {
            puts("0");
            continue;
        }
        memset(A,0,sizeof(A));
        if(k>n) swap(k,n); 
        M=1<<k;
        A[0][M-1]=1;
        for(int i=1;i<=n;i++)
            dfs(0,0,0,i);
        LL ans=A[n][M-1];
        cout<<ans<<endl;
    }
} 
Lv. 2

 

 

未解决

题目链接

棋盘规模 n*m  (n,m<=100)

唐老师博客

感觉这题应该不会考吧,出题人似乎是看过论文之后,强行按照论文出了个题

 

题目链接

棋盘规模 n*m  (n,m<=16)

稳定/免分割线多米诺骨牌的棋盘覆盖问题

要利用前面的Lv. 2的方法打表,然后用容斥原理解决,目前还不是很懂

骨牌覆盖小结