第 i-2 列伸到 i-1 列的状态为 k , 是否能成功转移到 第 i-1 列伸到 i 列的状态为 j 。说白了就是怎么在 i - 2 以及 i - 1 列放了横木块之后,再在 i - 1 列 和第 i 列放置横木块是合法?
(1)首先肯定是不同行,即一行中只能由 i-1 列和 i 列组成的横木块,或者 i - 2 列和 i - 1 列放置的横木块,若同行,则会共用第 i - 1 列的木块。我们可以用 j & k == 0 来表示。其含义就是两个二进制数进行与操作,若两者同一位数同时为1(同一行放置了两块横木板),即为不合法,否则当两二进制数与为0,表示不从在同位为1,即满足了条件。
(2)其次,每一列,所有连续着空着的小方格必须是偶数个。这里我们用 st[ i | j ] 来表示。其中波尔(bool) st[M] 数组存储的是M种情况下,满足情况的是那些二进制数。而至于为什么是 i | j , 首先j表示 i - 2 列合法伸出的二进制表示,当 i 和 j 进行或操作,就可以把 i - 1 列的二进数表示出来。
现在我们来看一下代码
#include<bits/stdc++.h>usingnamespace std;typedeflonglong LL;constint N =12, M =1<< N;//这里使用位运算,表示2的N次方 int n , m;//f[i][j]表示 i-1列的方案数已经确定,从i-1列伸出,并且第i列的状态是j的所有方案数
LL f[N][M];bool st[M];
vector<int> state[M];intmain(){
while(cin >> n >> m , n || m){
for(int i =0; i <1<<n ; i++){
int cnt =0;//连续存放0的个数 bool value =true;for(int j =0; j < n; j++){
if(i >> j &1)//如果第j位填充了小方格 {
if( cnt &1)//如果cnt为奇数,则这一列不能放置竖的木块 {
value =false;break;}
cnt =0;}else cnt++;//继续为空,不放置小方格 }if( cnt &1) value =false;
st[i]= value;}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);//st[i|j]是为了后面转移看i-1列能不能填竖着的}}//初始化f 数组 memset(f ,0,sizeof f);
f[0][0]=1;//最开始第0列,只有全部都不伸这一种情况for(int i =1; i <= m ;i++){
for(int j =0; j <1<<n; j++){
for(auto k : state[j]){
f[i][j]+= f[i -1][k];}}}
cout << f[m][0]<< endl;}}
作者:chenxuanqi6@163.com
链接:https://www.acwing.com/activity/content/code/content/541644/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。