状态压缩--二进制表示
思想:只用横的1 * 2 的小方块来储存,并保证余下的方块都可以拆入竖的方块且,竖的方块只有一种摆放方式。所以所有的方案的即为横的方块的所有方案。
状态表示:f[i][j]
集合:第 i 列的第 j 种情况的所有集合。( j 用二进制表示第 i 列的情况, 用十进制数来储存)
如上图,第 i 列的 j 用二进制表示为 1001, 储存为 9 ;
合法状态(预处理):
①:第 i 列的所有的连续空白数量为偶数。
for(int i = 0; i < 1 << n; i ++ )
{
st[i] = false;
int cnt = 0;
for(int j = 0; j < n; j ++ )
{
if(i >> j & 1 && cnt & 1) break;
if(!(i >> j & 1)) cnt ++;
}
if(cnt%2 == 0) st[i] = true;
}
②:第 i - 1 列 与第 i - 2 列突出的方块不重合且连续空白为偶数。
for(int i = 0; i < 1 << n; i ++ )
{
state[i].clear();
for(int j = 0; j < 1 << n; j ++ )
if( (i & j) == 0 && st[i | j]) state[i].push_back(j);
}
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 12, M = 1 << N;
long long f[N][M];
vector<vector<int> > state(M);
bool st[M];
int n, m;int main()
{
while(cin >> n >> m, n || m)
{
//初始化, 第i列合法状态(间隔为偶数)
for(int i = 0; i < 1 << n; i ++ )
{
st[i] = false;
int cnt = 0;
for(int j = 0; j < n; j ++ )
{
if(i >> j & 1 && cnt & 1) break;
if(!(i >> j & 1)) cnt ++;
}
if(cnt%2 == 0) st[i] = true;
}
// 初始化, 判断第i - 1列与第i - 2列是否合法
for(int i = 0; i < 1 << n; i ++ )
{
state[i].clear();
for(int j = 0; j < 1 << n; j ++ )
if( (i & j) == 0 && st[i | j]) state[i].push_back(j);
}
// 开始dp -- f[i][j] 表示所有第i列 情况是 j 的所有集合
memset(f, 0 , sizeof(f) ) ;
f[0][0] = 1; // 第0列只有第 -1 列没有突出来才合法
for(int i = 1; i <= m; i ++ ) // 从第 1 列遍历到第 m - 1 列
{
for(int j = 0; j < 1 << n; j ++ )
{
for(auto t : state[j] )
f[i][j] += f[i - 1][t];
}
}
cout << f[m][0] << endl;
}
}