E31 状态压缩DP 蒙德里安的梦想
视频链接:https://www.bilibili.com/video/BV1cv411b7EG/
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=12, M=1<<N; bool st[M]; //st[i]存储合并列的状态i是否合法 long long f[N][M]; //f[i][j]表示摆放第i列,状态为j时的方案数 int main(){ int n, m; while(cin>>n>>m, n||m){ // 预处理:判断合并列的状态i是否合法 // 合并列即两列状态合并之意,对应后面的st[j|k] // 如果合并列的某行是1表示横放,是0表示竖放 // 如果合并列不存在连续的奇数个0,即为合法状态 for(int i=0; i< 1<<n; i++){ st[i]=true; int cnt=0; //记录合并列中连续0的个数 for(int j=0; j<n; j++){ if(i>>j & 1){ //如果是1 if(cnt & 1){ //如果连续0的个数是奇数 st[i]=false; break; //记录i不合法 } } else cnt++; //如果是0,记录0的个数 } if(cnt & 1) st[i]=false; //处理高位0的个数 } memset(f, 0, sizeof f); f[0][0]=1; for(int i=1; i<=m; i++) //阶段:枚举列 for(int j=0; j < 1<<n; j++) //状态:枚举第i列的状态 for(int k=0; k < 1<<n; k++) //状态:枚举第i-1列的状态 if((j&k)==0 && st[j|k]) //两列状态兼容:不出现重叠的1,不出现连续的奇数个0 f[i][j] += f[i-1][k]; printf("%lld\n",f[m][0]); //第m列不横放即答案 } return 0; }