骨牌问题(铺地砖),所有类型模板总结。

 

 

友情链接:LYQ学长关于骨牌问题的详细总结:https://blog.csdn.net/nyist_tc_lyq/article/details/70770015

以下是我自己总结的板子(自己手打的板子,质量应该没有问题,若哪个大佬Orz发现板子有问题,还望在评论留言)

类型一:宽m*长n类型 ,砖类型:1*2型地砖。(2<=m<=7 , 0<=n<=1e18) 总体复杂度2^(3*m)  *logn  (以二为底n的对数)

注意m>=8时,我的代码矩阵快速幂会爆栈,而且就算不爆栈也会超时。

 

#1143 : 骨牌覆盖问题·一:http://hihocoder.com/problemset/problem/1143

 

#1151 : 骨牌覆盖问题·二:http://hihocoder.com/problemset/problem/1151 

 

#1162 : 骨牌覆盖问题·三:http://hihocoder.com/problemset/problem/1162  (思路都在提示中,想学的可以看一下,DP状压)

模板:代码的复杂度没有算多组输入或T组数据

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int N=5e4+20;
const int M=(1<<7)+1;
const int mod=12357;
struct node
{
  int a[M][M];
  void mems()
  {
    mem(a,0);
  }
};
int d[M][M];
int k,n;
void dfs(int x,int y,int c)
{
  if(c==k)
  {
    d[y][x]=1;
    return ;
  }
  dfs(x<<1,(y<<1)+1,c+1);
  dfs((x<<1)+1,y<<1,c+1);
  if(c+2<=k)
    dfs((x<<2)+3,(y<<2)+3,c+2);
}
node a,ans;
void init()
{
  a.mems();
  ans.mems();
  int s=1<<k;
  for(int i=0; i<s; i++)
    for(int j=0; j<s; j++)
      a.a[i][j]=d[i][j];
  ans.a[0][s-1]=1;
}
node operator *(node A,node B)
{
  node C;
  C.mems();
  int s=1<<k;
  for(int i=0; i<s; i++)
    for(int j=0; j<s; j++)
      if(A.a[i][j])
        for(int h=0; h<s; h++)
        {
          C.a[i][h]+=A.a[i][j]*B.a[j][h];
          if(C.a[i][h]>= mod) C.a[i][h]%=mod;
//mod根据需要自己调整,若mod>=1e5,int都要变LL。若mod<2^32,LL改ULL
        }
  return C;
}
int pow(int x)
{
  init();
  int s=1<<k;
  while(x)
  {
    if(x&1) ans=ans*a;
    a=a*a;
    x/=2;
  }
  return ans.a[0][s-1];
}
int main()
{
  while(~scanf("%d%d",&k,&n))
  {//若k>n,则swap(n,k);
    mem(d,0);
    dfs(0,0,0);//这个只与k(宽度)有关,与n无关。(k变化后,运行一次dfs(0,0,0)即可)
    printf("%d\n",pow(n));
  }
}

类型二:宽m*长n类型 ,砖类型:1*2型砖,2*2中挖去一个1*1(简称L型砖)。(2<=m<=7 , 0<=n<=1e18) 总体复杂度2^(3*m)  *logn  (以二为底n的对数)  (复杂度够的话就可以用下面的板子)
注意m>=8时,我的代码矩阵快速幂会爆栈,而且就算不爆栈也会超时。

参考博客:http://www.cnblogs.com/keam37/p/3835098.html (2<=n,m<=9 这种题目也是一种题型,板子在下一类型中有介绍)

对于这种砖块类型的题,因为我实在是找不到那种m小,n非常大的题,所以只能找个数据范围不一样的题目,来测一下我的代码(对于上面的博客里的这道题http://acm.nyist.me/problem/435 我的模板只能特判一下m=8和m=9的情况了,这个板子主要是用来处理m不大,n非常大的题目

 

模板:代码的复杂度没有算多组输入或T组数据

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
typedef long long LL;
const LL N=5e4+20;
const LL M=130;
const LL mod=1e9+7;
struct node
{
    LL a[M][M];
    void mems()
    {
        mem(a,0);
    }
};
LL d[M][M];
LL k,n;
void dfs(LL s1,LL s2,LL b1,LL b2,LL c)
{
    if(c==k)
    {
        if(b1==0&&b2==0) d[s1][s2]++;
        return ;
    }
    if(b1==0&&b2==0)
    {
        dfs(s1<<1,s2<<1|1,0,0,c+1);
        dfs(s1<<1,s2<<1|1,0,1,c+1);
        dfs(s1<<1,s2<<1|1,1,0,c+1);
    }
    if(b2==0)
    {
        dfs(s1<<1|(1-b1),s2<<1|1,0,1,c+1);
        dfs(s1<<1|(1-b1),s2<<1|1,1,1,c+1);
    }
    if(b1==0)
        dfs(s1<<1,s2<<1|b2,1,1,c+1);
    dfs(s1<<1|(1-b1),s2<<1|b2,0,0,c+1);
}
node a,ans;
void init()
{
    a.mems();
    ans.mems();
    LL s=1<<k;
    for(LL i=0; i<s; i++)
        for(LL j=0; j<s; j++)
            a.a[i][j]=d[i][j];
    ans.a[0][s-1]=1;
}
node operator *(const node &A,const node &B)
{
    node C;
    C.mems();
    LL s=1<<k;
    for(LL i=0; i<s; i++)
        for(LL j=0; j<s; j++)
            if(A.a[i][j])
                for(LL h=0; h<s; h++)
                {
                    C.a[i][h]+=A.a[i][j]*B.a[j][h];
                    if(C.a[i][h]>= mod) C.a[i][h]%=mod;
//mod根据需要自己调整,若mod>=1e5,int都要变LL。若mod<2^32,LL改ULL
                }
    return C;
}
LL pow(LL x)
{
    init();
    while(x)
    {
        if(x&1) ans=ans*a;
        a=a*a;
        x/=2;
    }
    return ans.a[0][(1<<k)-1];
}
int main()
{
    while(~scanf("%lld%lld",&k,&n))
    {
        //若k>n,则swap(k,n);
        mem(d,0);
        dfs(0,0,0,0,0);//这个只与k(宽度)有关,与n无关。(k变化后,运行一次dfs(0,0,0,0,0)即可)
        printf("%lld\n",pow(n));
    }
}

 

类型三:宽m*长n类型 ,砖类型:1*2型砖,2*2中挖去一个1*1(简称L型砖)。(2<=m<=11,n<=20) 总体复杂度2^(2*m)*n  实际的复杂度是要比这个式子小不少的,  自己根据复杂度判断题目是否属于此类型 (题目若有mod,自己在计算过程中添加取余)

参考博客:http://www.cnblogs.com/keam37/p/3835098.html 

m=11,n=11,这样的数据大约跑了500+ms,还算是比较快的。

模板代码:

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#define LL long long
using namespace std;
const int N=12;
const int M=12;
int n,m,x;
LL f[N][(1<<M)+10];
void dfs (int k, int last, int now, int b1, int b2)
{
    if(k==m)
    {
        if(!b1&&!b2)
            f[x][now]+=f[x-1][last];
        return;
    }
    if(!b1&&!b2)
    {
        dfs(k+1,last<<1,now<<1|1,0,0);
        dfs(k+1,last<<1,now<<1|1,1,0);
        dfs(k+1,last<<1,now<<1|1,0,1);
    }
    if(!b1)
        dfs(k+1,last<<1,now<<1|b2,1,1);
    if(!b2)
    {
        dfs(k+1,last<<1|(1-b1),now<<1|1,0,1);
        dfs(k+1,last<<1|(1-b1),now<<1|1,1,1);
    }
    dfs(k+1,last<<1|(1-b1),now<<1|b2,0,0);
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(f,0,sizeof(f));
        if(n<m) swap(n,m);
        f[0][(1<<m)-1]=1;
        for (x=1; x<=n; x++)
            dfs(0,0,0,0,0);
        printf("%lld\n",f[n][(1<<m)-1]);
        return 0;
    }
}

 

类型三(跟上面的问题一样,这个板子效率要比上面的效率高很多很多,用的算法是轮廓线):宽m*长n类型 ,砖类型:1*2型砖,2*2中挖去一个1*1(简称L型砖)。(2<=m<=15,n<=20)
总体复杂度(2^m )*n*m ,  自己根据复杂度判断题目是否可用此模板

 

 m=11,n=11,这样的数据大约跑了100+ms,这个代码就跑的飞快了。

题目链接:http://acm.nyist.me/problem/435

此代码为GYR大神写的:(算法:轮廓线)

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <cmath>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
typedef long long ll;
const int N=12;
ll dp[2][(1<<N)+10]; //N=min(n,m);
int main()
{
    int n,m,cur;
    while(~scanf("%d%d",&n,&m))
    {
        if(n<m)swap(n,m);
        cur=0;
        dp[0][(1<<m+1)-1]=1;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                cur^=1;
                mem(dp[cur],0);
                for(int k=0; k<1<<m+1; k++)
                {
                    if(j&&(k&(1<<m))&&!(k&1))//竖着放1*2的
                        dp[cur][((k^(1<<m))<<1)|3]+=dp[1-cur][k];
                    if(i&&(k&(1<<m))&&!(k&(1<<m-1)))//横着放1*2的
                        dp[cur][((k^(1<<m)|(1<<m-1))<<1)|1]+=dp[1-cur][k];
                    if(k&(1<<m))//不放
                        dp[cur][(k^(1<<m))<<1]+=dp[1-cur][k];
                    if(i&&j&&!(k&(1<<m))&&!(k&1))//第一种L型砖
                        dp[cur][k<<1|3]+=dp[1-cur][k];
                    if(i&&j&&(k&(1<<m))&&!(k&(1<<m-1))&&!(k&1))//第二种L型砖
                        dp[cur][(k^(1<<m)|(1<<m-1))<<1|3]+=dp[1-cur][k];
                    if(i&&(j!=m-1)&&(k&(1<<m))&&!(k&(1<<m-1))&&!(k&(1<<m-2)))//第三种L型砖
                        dp[cur][(k^(1<<m)|(3<<m-2))<<1|1]+=dp[1-cur][k];
                    if(i&&j&&!(k&(1<<m))&&!(k&(1<<m-1)))//第四种L型砖
                        dp[cur][(k<<1)|(1<<m)|1]+=dp[1-cur][k];
                }
            }
        }
        printf("%lld\n",dp[cur][(1<<m+1)-1]);
    }
    return 0;
}

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值