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