传送门
这种放置类的状压dp一般是按行划分阶段.状态之间的转移一般依据题目给的限制.例如这题提供了2种1×2的方块.横放的方块对下一行是没有影响的,只有竖放的方块才能影响到下一行.所以我们用1来表示竖放方块的上半部分,用0表示其他状态.
再考虑不合法的状态,显然i-1和i行在第j列不能同时为1,可以直接用&来进行运算.还要注意的是如果第j列同时为0.说明至少有一行会横放方块.所以要有连续的偶数个0才可以放置.这一步用|来进行运算.
到这里复杂度还是有点大,因为我们要对|运算的结果进行判断是否都是连续的偶数个0.可以进行优化.提前把这些状态处理好.达到O(1)的判断时间.
如果要在一步进行优化的话,可以发现每行之间进行的匹配都是一样的,可以只存合法的转移达到进一步优化的目的(测试了一下会快大概一倍).
代码:
#pragma GCC optimize(2)
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#include <bits/stdc++.h>
using namespace std;
const int N = 12;
inline void read(int &a){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){
x=x*10+ch-'0';ch=getchar();}
a = x*f;
}
LL dp[N][1<<N];
bool judge[1<<N];
int main(){
int n,m;
while(cin >> n >> m,n&&m){
mem(dp,0);
dp[0][0] = 1;
fir(i,0,(1<<m)-1){
int cnt = 0;
bool f = 1;
fir(j,0,m-1){
int bit = (i >> j) & 1;
if(bit){
if(cnt % 2){
f = 0;
break;
}
cnt = 0;
}
else{
cnt++;
}
}
if(cnt%2) f= 0;
judge[i] = f;
}
fir(i,1,n){
fir(j,0,(1<<m)-1){
fir(k,0,(1<<m)-1){
if(!(j & k) && judge[j|k]){
dp[i][j] += dp[i-1][k];
}
}
}
}
cout << dp[n][0] << endl;
}
return 0;
}