友情链接: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;
}