题意
有很多个2*1的骨牌,放到N*M的棋盘中,问有多少种铺满棋盘的方法。
题解
对于这道题,由于骨牌涉及了多行,所以利用传统的状压DP是无法解决的。在这里需要运用一种叫轮廓线DP的技巧。轮廓线DP不再是针对某一行或者某一列进行状态转移,而是以每一个元素为中心,进行状态转移。
很明显,对于一个位置有三种选择,不放,横放,竖放。对于不放这种选择来说,只需要保证上一列这个位置的元素不为空就可以进行状态转移。对于横放这种选择来说,首先要保证当前状态左边的一个元素是空的,这样的话就可以进行状态转移。对于竖放这种选择来说,要保证上面一个元素是空的,这样才能进行状态转移。
注意事项
注意INT溢出
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXN 120050
#define MOD 1000000007
#define EPS 1e-3
using namespace std;
LL dp[2][2050];
int cnt;
int n,m;
void update(int a,int b){
if(b&(1<<m)){dp[cnt][b^(1<<m)]+=dp[cnt^1][a];}
}
int main(){
W(~scanf("%d%d",&n,&m)){
if(m>n) swap(n,m);
cnt=0;
MEM(dp,0);
dp[cnt][(1<<m)-1]=1;
UP(i,0,n){
UP(j,0,m){
cnt^=1;
MEM(dp[cnt],0);
UP(k,0,(1<<m)){
update(k,k<<1);
if(i&&!(k&(1<<(m-1)))) update(k,(k<<1)^1^(1<<m));
if(j&&!(k&1)) update(k,(k<<1)^3);
}
}
}
printf("%lld\n",dp[cnt][(1<<m)-1]);
}
}